<!DOCTYPE html>



  


<html class="theme-next gemini use-motion" lang="zh-Hans">
<head>
  <meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
<meta name="theme-color" content="#222">









<meta http-equiv="Cache-Control" content="no-transform" />
<meta http-equiv="Cache-Control" content="no-siteapp" />
















  
  
  <link href="/blog/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css" />




  
  
  
  

  
    
    
  

  
    
      
    

    
  

  

  
    
      
    

    
  

  
    
      
    

    
  

  
    
    
    <link href="//fonts.googleapis.com/css?family=Monda:300,300italic,400,400italic,700,700italic|Roboto Slab:300,300italic,400,400italic,700,700italic|PT Mono:300,300italic,400,400italic,700,700italic|PT Mono:300,300italic,400,400italic,700,700italic&subset=latin,latin-ext" rel="stylesheet" type="text/css">
  






<link href="/blog/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css" />

<link href="/blog/css/main.css?v=5.1.4" rel="stylesheet" type="text/css" />


  <link rel="apple-touch-icon" sizes="180x180" href="/blog/images/apple-touch-icon-next.png?v=5.1.4">


  <link rel="icon" type="image/png" sizes="32x32" href="/blog/images/favicon-32x32-next.png?v=5.1.4">


  <link rel="icon" type="image/png" sizes="16x16" href="/blog/images/favicon-16x16-next.png?v=5.1.4">


  <link rel="mask-icon" href="/blog/images/logo.svg?v=5.1.4" color="#222">





  <meta name="keywords" content="Hexo, NexT" />










<meta name="description" content="JUST DO IT.">
<meta property="og:type" content="website">
<meta property="og:title" content="Srtian&#39;Blog">
<meta property="og:url" content="http://srtian96.gitee.io/blog/index.html">
<meta property="og:site_name" content="Srtian&#39;Blog">
<meta property="og:description" content="JUST DO IT.">
<meta property="og:locale" content="zh-Hans">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Srtian&#39;Blog">
<meta name="twitter:description" content="JUST DO IT.">



<script type="text/javascript" id="hexo.configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    root: '/blog/',
    scheme: 'Gemini',
    version: '5.1.4',
    sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false},
    fancybox: true,
    tabs: true,
    motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},
    duoshuo: {
      userId: '0',
      author: '博主'
    },
    algolia: {
      applicationID: '',
      apiKey: '',
      indexName: '',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    }
  };
</script>



  <link rel="canonical" href="http://srtian96.gitee.io/blog/"/>





  <title>Srtian'Blog</title>
  








</head>

<body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans">

  
  
    
  

  <div class="container sidebar-position-left 
  page-home">
    <div class="headband"></div>

    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-wrapper">
  <div class="site-meta ">
    

    <div class="custom-logo-site-title">
      <a href="/blog/"  class="brand" rel="start">
        <span class="logo-line-before"><i></i></span>
        <span class="site-title">Srtian'Blog</span>
        <span class="logo-line-after"><i></i></span>
      </a>
    </div>
      
        <p class="site-subtitle">It's better to burn out than to fade away</p>
      
  </div>

  <div class="site-nav-toggle">
    <button>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
    </button>
  </div>
</div>

<nav class="site-nav">
  

  
    <ul id="menu" class="menu">
      
        
        <li class="menu-item menu-item-home">
          <a href="/blog/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-home"></i> <br />
            
            首页
          </a>
        </li>
      
        
        <li class="menu-item menu-item-about">
          <a href="/blog/about/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-user"></i> <br />
            
            关于
          </a>
        </li>
      
        
        <li class="menu-item menu-item-tags">
          <a href="/blog/tags/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-tags"></i> <br />
            
            标签
          </a>
        </li>
      
        
        <li class="menu-item menu-item-categories">
          <a href="/blog/categories/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-th"></i> <br />
            
            分类
          </a>
        </li>
      
        
        <li class="menu-item menu-item-archives">
          <a href="/blog/archives/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-archive"></i> <br />
            
            归档
          </a>
        </li>
      

      
        <li class="menu-item menu-item-search">
          
            <a href="javascript:;" class="popup-trigger">
          
            
              <i class="menu-item-icon fa fa-search fa-fw"></i> <br />
            
            搜索
          </a>
        </li>
      
    </ul>
  

  
    <div class="site-search">
      
  <div class="popup search-popup local-search-popup">
  <div class="local-search-header clearfix">
    <span class="search-icon">
      <i class="fa fa-search"></i>
    </span>
    <span class="popup-btn-close">
      <i class="fa fa-times-circle"></i>
    </span>
    <div class="local-search-input-wrapper">
      <input autocomplete="off"
             placeholder="搜索..." spellcheck="false"
             type="text" id="local-search-input">
    </div>
  </div>
  <div id="local-search-result"></div>
</div>



    </div>
  
</nav>



 </div>
    </header>

    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          <div id="content" class="content">
            
  <section id="posts" class="posts-expand">
    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://srtian96.gitee.io/blog/blog/2019/02/17/WebGL漫游之旅（一）/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="Srtian">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/blog/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Srtian'Blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/blog/2019/02/17/WebGL漫游之旅（一）/" itemprop="url">WebGL漫游之旅（一）</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2019-02-17T19:17:39+08:00">
                2019-02-17
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/可视化/" itemprop="url" rel="index">
                    <span itemprop="name">可视化</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          
            <div class="post-wordcount">
              
                
                <span class="post-meta-item-icon">
                  <i class="fa fa-file-word-o"></i>
                </span>
                
                  <span class="post-meta-item-text">字数统计&#58;</span>
                
                <span title="字数统计">
                  2,505
                </span>
              

              
                <span class="post-meta-divider">|</span>
              

              
                <span class="post-meta-item-icon">
                  <i class="fa fa-clock-o"></i>
                </span>
                
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                
                <span title="阅读时长">
                  10
                </span>
              
            </div>
          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <h3 id="一、WebGL基本概念"><a href="#一、WebGL基本概念" class="headerlink" title="一、WebGL基本概念"></a>一、WebGL基本概念</h3><blockquote>
<p>WebGL (Web Graphics Library) is a JavaScript API for rendering interactive 3D and 2D graphics within any compatible web browser without the use of plug-ins. WebGL does so by introducing an API that closely conforms to OpenGL ES 2.0 that can be used in HTML5 canvas elements.  –MDN</p>
</blockquote>
<p>以上是MDN对于WebGL的描述，简单来说，WebGL 就是一组基于 JavaScript 语言的图形规范，浏览器厂商按照这组规范进行实现，为 Web 开发者提供一套3D图形相关的 API。</p>
<p>我们可以通过这些API直接使用JavaScript直接和GPU进行通信，从而实现一些非常炫酷的图形。而webGL是在GPU上运行的，因此我们需要使用能够在GPU上运行的代码，首先我们需要一种叫做GLSL的语言，它是一种和C or CPP类似的强类型的语言，所以写起来很麻烦（这也是很多人吐槽WebGL的一个方面），其次这样的代码需要提供成对的方法，每对方法中，一个叫做顶点着色器，一个叫做片段着色器，这样的每一对组合起来就称作一个program(着色程序)。其中顶点着色器的作用是计算顶点的位置，根据计算出来的一系列的顶点的位置，WebGL就可以对点、线以及三角形在内的一些图元进行光栅化处理。当对这些图元进行光栅化处理的时候就需要使用片段着色器方法了，它的作用是计算出当前绘制图元中的每个像素的颜色值。</p>
<h4 id="1-1-什么是GLSL"><a href="#1-1-什么是GLSL" class="headerlink" title="1.1 什么是GLSL"></a>1.1 什么是GLSL</h4><p>上面我们提到了GLSL，其中文的意思是OpenGL着色语言，它是用来在 OpenGL 编写着色器程序的语言，全称为 OpenGL Shading Language。而着色器程序则是在GPU上运行的简短的程序，代替了GPU固定渲染管线的一部分，使GPU渲染过程中的某些部分允许开发者通过编程进行控制。</p>
<p>而GPU渲染过程中具体允许我们对其进行控制的部分有以下几个方面：</p>
<ul>
<li>JavaScript程序，处理着色器所需要的顶点坐标、法向量、颜色、纹理等。</li>
<li>顶点着色器，接受JavaScript传递过来的顶点信息，将顶点绘制到对应的坐标。</li>
<li>图元装配阶段，将三个顶点装配成指定的图元类型。</li>
<li>光栅化阶段，将三角形内部区域用空像素进行填充。</li>
<li>片元着色器，为三角形内部的像素填充颜色信息。</li>
</ul>
<h4 id="1-2-WebGL工作流程"><a href="#1-2-WebGL工作流程" class="headerlink" title="1.2 WebGL工作流程"></a>1.2 WebGL工作流程</h4><p>上面对WebGL的基本情况进行了一个简单的概述，但好像也没有解答webGL将3D模型显示到屏幕上的工作原理及流程。其实这个过程就好比富士康工作流水一样，按照既定的工作流程来对原材料进行加工，从而生产出完整的产品。WebGL大致也是如此，按照工作流水线的方式，将3D的模型数据渲染到2D屏幕上的，这个渲染方式的过程一般被称之为图形管线或者渲染管线。</p>
<p>上面我们又说到过点、线、三角形这些基本图元，但我们经常看见很多通过WebGL所绘制出来的诸如球体、圆柱、各式的立方体等模型，也看见了很多炫酷、复杂的模型，很显然这些并不属于这些基本图元里面，但其实这些模型本质上都是有一个个顶点组成的，GPU将这些点用三角线图元绘制成一个个微小的小平面，然后通过这些小平面的互相连接，来组成各种各样的的立体模型。因此通常来说，我们首先要做的就是创建组成模型的顶点数据。</p>
<p>一般情况下，最初的顶点坐标是相对于模型中心的，我们需要对顶点坐标按照一系列步骤执行模型转换、视图转换、投影转换，在通过这一系列的转换后的坐标叫做裁剪空间坐标，这个坐标才是WebGL可以接受的坐标。我们把最后的变换矩阵和原始顶点坐标传递给GPU，GPU的渲染管线然后对他们执行流水工作，主要过程如下：</p>
<ol>
<li>进入顶点着色器，利用GPU的并行计算优势对顶点逐个进行坐标变换。</li>
<li>进入图元装配阶段，将会顶点按照图元类型组装成图形</li>
<li>进入光栅化阶段，光栅化阶段对图像用不包含颜色信息的像素进行填充</li>
<li>进入着色器阶段，为像素着色，并最终显示在屏幕上</li>
</ol>
<h3 id="二、WebGL初体验"><a href="#二、WebGL初体验" class="headerlink" title="二、WebGL初体验"></a>二、WebGL初体验</h3><p>上面将WebGL的大致情况进行了描述，下面就是真刀实枪的来搞事情了，和Three.js一样（这是废话。），我们在使用WebGL进行开发的时候首先需要使用canvas，我们可以再HTML文件里的这样声明一个canvas。顺便对浏览器对canvas的支持情况进行一个检查：<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">body</span> <span class="attr">onload</span>=<span class="string">"main()"</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">canvas</span> <span class="attr">id</span>=<span class="string">"glcanvas"</span> <span class="attr">width</span>=<span class="string">"640"</span> <span class="attr">height</span>=<span class="string">"480"</span>&gt;</span></span><br><span class="line">    Your browser doesn't appear to support the HTML5 <span class="tag">&lt;<span class="name">code</span>&gt;</span>&amp;lt;canvas&amp;gt;<span class="tag">&lt;/<span class="name">code</span>&gt;</span> element.</span><br><span class="line">  <span class="tag">&lt;/<span class="name">canvas</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br></pre></td></tr></table></figure></p>
<p>webGL应用主要包含两个要素：JavaScript程序和着色器程序。首先让我们来准备着色器程序，使用GLSL编写顶点着色器和片元着色器。</p>
<p>顶点着色器的任务我们在上面已经说了，它主要是告诉GPU我们所要形成的图形在裁剪坐标系的位置，下面这个代码就是告诉GPU我们需要在裁剪坐标系的原点，即屏幕中心画一个大小为20的点：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span> main()&#123;</span><br><span class="line">    <span class="comment">//声明顶点位置</span></span><br><span class="line">    gl_Position = vec4(<span class="number">0.0</span>, <span class="number">0.0</span>, <span class="number">0.0</span>, <span class="number">1.0</span>)</span><br><span class="line">    <span class="comment">//声明所需绘制的点的大小</span></span><br><span class="line">    gl_PointSize = <span class="number">20.0</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>当顶点着色器中的数据经过图元装配和光栅化之后，来到了片元着色器，从而通过片元着色器将像素渲染成我们所需要的颜色：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span> main()&#123;</span><br><span class="line">    <span class="comment">//设置像素的填充颜色为红色。</span></span><br><span class="line">    gl_FragColor = vec4(<span class="number">1.0</span>, <span class="number">0.0</span>, <span class="number">0.0</span>, <span class="number">1.0</span>) </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>在这里，gl_Position、gl_PointSize、gl_FragColor 是 GLSL 的内置属性：</p>
<ul>
<li>gl_Position：顶点的裁剪坐标系坐标，包含X、Y、Z、W四个坐标分量。顶点着色器接收坐标后，会对它进行透视除法，即将各个分量同时除以 W，从而转换成 NDC 坐标，NDC 坐标每个分量的取值范围都在（-1, 1）之间，GPU 获取这个属性值作为顶点的最终位置进行绘制。</li>
<li>gl_FragColor：片元（像素）颜色，包含 R, G, B, A 四个颜色分量，且每个分量的取值范围在（0,1）之间，GPU 会获取这个值，作为像素的最终颜色进行着色。</li>
<li>gl_PointSize：绘制到屏幕的点的大小，gl_PointSize只有在绘制图元是点的时候才会生效。</li>
</ul>
<p>然后我们就可以着手写我们的JavaScript部分的代码了，首先我们需要获取webGL的绘图环境：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> canvas = <span class="built_in">document</span>.querySelector(<span class="string">'#canvas'</span>)</span><br><span class="line"><span class="keyword">const</span> gl = canvas.getContext(<span class="string">'webgl'</span>)</span><br></pre></td></tr></table></figure>
<p>然后创建顶点着色器：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 获取顶点着色器源码</span></span><br><span class="line"><span class="keyword">const</span> vertexShaderSource = <span class="built_in">document</span>.querySelector(<span class="string">'#vertexShader'</span>).innerHTML</span><br><span class="line"><span class="comment">// 创建顶点着色器对象</span></span><br><span class="line"><span class="keyword">const</span> vertexShader = gl.createShader(gl.VERTEX_SHADER)</span><br><span class="line"><span class="comment">// 将源码分配给顶点着色器对象</span></span><br><span class="line">gl.shaderSource(vertexShader, vertexShaderSource)</span><br><span class="line"><span class="comment">// 编译顶点着色器程序</span></span><br><span class="line">gl.compileShader(vertexShader)</span><br></pre></td></tr></table></figure>
<p>再就是创建片元着色器：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 获取片元着色器源码</span></span><br><span class="line"><span class="keyword">const</span> fragmentShaderSource = <span class="built_in">document</span>.querySelector(<span class="string">'#fragmentShader'</span>).innerHTML</span><br><span class="line"><span class="comment">// 创建片元着色器程序</span></span><br><span class="line"><span class="keyword">const</span> fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)</span><br><span class="line"><span class="comment">// 将源码分配给片元着色器对象</span></span><br><span class="line">gl.shaderSource(fragmentShader, fragmentShaderSource)</span><br><span class="line"><span class="comment">// 编译片元着色器</span></span><br><span class="line">gl.compileShader(fragmentShader)</span><br></pre></td></tr></table></figure>
<p>以上就将我们的着色器对象创建完成了，接下来我们就可以创建着色器程序了：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//创建着色器程序</span></span><br><span class="line"><span class="keyword">const</span> program = gl.createProgram()</span><br><span class="line"><span class="comment">//将顶点着色器挂载在着色器程序上。</span></span><br><span class="line">gl.attachShader(program, vertexShader)</span><br><span class="line"><span class="comment">//将片元着色器挂载在着色器程序上。</span></span><br><span class="line">gl.attachShader(program, fragmentShader)</span><br><span class="line"><span class="comment">//链接着色器程序</span></span><br><span class="line">gl.linkProgram(program)</span><br></pre></td></tr></table></figure>
<p>我们在进行webgl开发的时候，可能会在一个WebGL应用里包含多个program，因此我们在使用某狗program绘制前，要先启用它，才能进行绘制：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">gl.useProgram(program)</span><br><span class="line"><span class="comment">// 绘制</span></span><br><span class="line">gl.clearColor(<span class="number">0.0</span>, <span class="number">0.0</span>, <span class="number">0.0</span>, <span class="number">1.0</span>)</span><br><span class="line">gl.clear(gl.COLOR_BUFFER_BIT)</span><br><span class="line">gl.drawArrays(gl.POINTS, <span class="number">0</span>, <span class="number">1</span>)</span><br></pre></td></tr></table></figure>
<p>如此我们完成了我们的第一个webGL代码了，效果如下：<br><img src="https://upload-images.jianshu.io/upload_images/4116027-a3e4b13954be668d.PNG?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="webGL1.PNG"><br>完整代码如下：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line">&lt;body onload=<span class="string">"main()"</span>&gt;</span><br><span class="line">	&lt;!-- 顶点着色器源码 --&gt;</span><br><span class="line">	&lt;script type=<span class="string">"shader-source"</span> id=<span class="string">"vertexShader"</span>&gt;</span><br><span class="line">	 <span class="keyword">void</span> main()&#123;</span><br><span class="line">  		<span class="comment">//声明顶点位置</span></span><br><span class="line">  		gl_Position = vec4(<span class="number">0.0</span>, <span class="number">0.0</span>, <span class="number">0.0</span>, <span class="number">1.0</span>);</span><br><span class="line">  		<span class="comment">//声明要绘制的点的大小。</span></span><br><span class="line">  		gl_PointSize = <span class="number">10.0</span>;</span><br><span class="line">  	&#125;</span><br><span class="line">	&lt;<span class="regexp">/script&gt;</span></span><br><span class="line"><span class="regexp">	</span></span><br><span class="line"><span class="regexp">	&lt;!-- 片元着色器源码 --&gt;</span></span><br><span class="line"><span class="regexp">	&lt;script type="shader-source" id="fragmentShader"&gt;</span></span><br><span class="line"><span class="regexp">	 void main()&#123;</span></span><br><span class="line"><span class="regexp">	 	/</span><span class="regexp">/设置像素颜色为红色</span></span><br><span class="line"><span class="regexp">		gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); </span></span><br><span class="line"><span class="regexp">	&#125;</span></span><br><span class="line"><span class="regexp">	&lt;/</span>script&gt;</span><br><span class="line">	&lt;canvas id=<span class="string">"canvas"</span> width=<span class="string">"640"</span> height=<span class="string">"480"</span>&gt;</span><br><span class="line">	Your browser doesn<span class="string">'t appear to support the HTML5 &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; element.</span></span><br><span class="line"><span class="string">	&lt;/canvas&gt;</span></span><br><span class="line"><span class="string">&lt;script type="text/javascript"&gt;</span></span><br><span class="line"><span class="string">function main() &#123;</span></span><br><span class="line"><span class="string">	// 获取webGL的绘图环境</span></span><br><span class="line"><span class="string">	const canvas = document.querySelector("#canvas")</span></span><br><span class="line"><span class="string">  	const gl = canvas.getContext("webgl")</span></span><br><span class="line"><span class="string">	// 创建顶点着色器</span></span><br><span class="line">	const vertexShaderSource = document.querySelector('#vertexShader').innerHTML</span><br><span class="line">	<span class="keyword">const</span> vertexShader = gl.createShader(gl.VERTEX_SHADER)</span><br><span class="line">	gl.shaderSource(vertexShader, vertexShaderSource)</span><br><span class="line">	gl.compileShader(vertexShader)</span><br><span class="line">	<span class="comment">// 创建片元着色器</span></span><br><span class="line">	<span class="keyword">const</span> fragmentShaderSource = <span class="built_in">document</span>.querySelector(<span class="string">'#fragmentShader'</span>).innerHTML</span><br><span class="line">	<span class="keyword">const</span> fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)</span><br><span class="line">	gl.shaderSource(fragmentShader, fragmentShaderSource)</span><br><span class="line">	gl.compileShader(fragmentShader)</span><br><span class="line">	<span class="comment">// 创建着色器程序</span></span><br><span class="line">	<span class="keyword">const</span> program = gl.createProgram()</span><br><span class="line">	gl.attachShader(program, vertexShader)</span><br><span class="line">	gl.attachShader(program, fragmentShader)</span><br><span class="line">	gl.linkProgram(program)</span><br><span class="line"></span><br><span class="line">	gl.useProgram(program)</span><br><span class="line">	<span class="comment">// 绘制</span></span><br><span class="line">	gl.clearColor(<span class="number">0.0</span>, <span class="number">0.0</span>, <span class="number">0.0</span>, <span class="number">1.0</span>)</span><br><span class="line">	gl.clear(gl.COLOR_BUFFER_BIT)</span><br><span class="line">	gl.drawArrays(gl.POINTS, <span class="number">0</span>, <span class="number">1</span>)</span><br><span class="line">&#125;</span><br><span class="line">&lt;<span class="regexp">/script&gt;</span></span><br><span class="line"><span class="regexp">&lt;/</span>body&gt;</span><br></pre></td></tr></table></figure>
<p>参考资料：</p>
<ul>
<li><a href="https://webglfundamentals.org/webgl/lessons/zh_cn/webgl-fundamentals.html" target="_blank" rel="noopener">https://webglfundamentals.org/webgl/lessons/zh_cn/webgl-fundamentals.html</a></li>
<li><a href="https://developer.mozilla.org/zh-CN/docs/Web/API/WebGL_API/Tutorial/Getting_started_with_WebGL" target="_blank" rel="noopener">https://developer.mozilla.org/zh-CN/docs/Web/API/WebGL_API/Tutorial/Getting_started_with_WebGL</a></li>
<li><a href="https://juejin.im/book/5baaf635f265da0ab915cc9f/section/5baaf635e51d450e7125482f" target="_blank" rel="noopener">https://juejin.im/book/5baaf635f265da0ab915cc9f/section/5baaf635e51d450e7125482f</a></li>
</ul>

          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://srtian96.gitee.io/blog/blog/2019/02/13/Web语义化的那些事儿/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="Srtian">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/blog/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Srtian'Blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/blog/2019/02/13/Web语义化的那些事儿/" itemprop="url">Web语义化的那些事儿</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2019-02-13T17:19:51+08:00">
                2019-02-13
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/浏览器/" itemprop="url" rel="index">
                    <span itemprop="name">浏览器</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          
            <div class="post-wordcount">
              
                
                <span class="post-meta-item-icon">
                  <i class="fa fa-file-word-o"></i>
                </span>
                
                  <span class="post-meta-item-text">字数统计&#58;</span>
                
                <span title="字数统计">
                  2,347
                </span>
              

              
                <span class="post-meta-divider">|</span>
              

              
                <span class="post-meta-item-icon">
                  <i class="fa fa-clock-o"></i>
                </span>
                
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                
                <span title="阅读时长">
                  9
                </span>
              
            </div>
          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <h3 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h3><p>本来很早前我就自认为对前端语义化有了一些了解，但在看完winter大大的专栏后才发现，自己对语义化的理解并不是很透彻，只存在于表面的理解而已，因此决定在学习winter老师的专栏后结合自己的一些理解，好好对其进行进行一个总结。</p>
<h3 id="一、语义化是什么"><a href="#一、语义化是什么" class="headerlink" title="一、语义化是什么"></a>一、语义化是什么</h3><p>从维基百科所得到的结果：</p>
<blockquote>
<p>The Semantic Web provides a common framework that allows data to be shared and reused across application, enterprise, and community boundaries. –Wikipedia</p>
</blockquote>
<p>Web语义化提供了一个通用框架，允许跨应用的程序，企业和社区共享和复用数据。简单来说，Web语义化是指使用恰当语义的html标签、class类名等内容，让页面具有良好的结构与含义，从而让人和机器都能快速理解网页内容。</p>
<p>我们都知道，对于Web来说，HTML就是负责联系大部分的Web资源的承载体和纽带，也就是我们常说的<strong>内容</strong>的载体。但在web刚被设计出来之初，设计者也没想到web会达到现在如此巨大的规模，因此早期的的HTML标准中所提供的元素也并不多，只有常见的h1-h6、ul、ol等标签。但随着Web的快速发展，网页的数量以及开发规模越来越大，为了能让用户在使用搜索引擎时能够更快、更精确的定位所要查询的网页，以及减少一些开发的维护余合作的成本，HTML在后面又提供了诸多的语义化标签，从而让页面有更为良好的结构，让搜索引擎可以更好的去查询定位网页，同时也使人更易于去理解网页的结构，这在团队协作开发中很重要。</p>
<h3 id="二、为什么要语义化"><a href="#二、为什么要语义化" class="headerlink" title="二、为什么要语义化"></a>二、为什么要语义化</h3><p>既然知道了上面是语义化，也大概的知晓的语义化为我们带来的一些好处，但这些好处对于我们来说好像并不够具体。因为好像我们在平常进行开发的时候就只使用了div和span，也能写出一个完整的页面出来，简称span、div一把嗦，这也是我平时的做法（当然，现在在使用react的时候更加如此了）。我一直觉得这种做法其实挺不好的，至少挺low的，毕竟很多类似的文章都告诉我们，要做好web语义化，做好SEO。但在winter老师的专栏中又对此进行了分析：</p>
<blockquote>
<p>这样做行不行呢？毫无疑问答案是行。那这样做好不好呢？按照正确的套路，我应该说不好，但是在很多情况下，答案其实是好。<br>这是因为在现代互联网产品里， HTML 用于描述 “ 软件界面 ” 多过于 “ 富文本 ” ，而软件界面里的东西，实际上几乎是没有语义的。比如说，我们做了一个购物车功能，我们一定要给每个购物<br>车里的商品套上 ul 吗？比如说，加入购物车这个按钮，我们一定要用 Button 吗？<br>实际上我觉得没必要，因为这个场景里面，跟文本中的列表，以及表单中的 Button ，其实已经相差很远了，所以，我支持在任何 “ 软件界面 ” 的场景中，直接使用 div 和 span 。</p>
</blockquote>
<p>但话虽这样说，winter老师所说的前提是在软件界面的条件下如此，如果是其他的工作场景，语义类的标签就拥有其自身的优点了：</p>
<ul>
<li>增强代码可读性，有利于团队协作与维护。</li>
<li>有利于SEO</li>
<li>支持读屏软件，可以自动生成目录。</li>
<li>无CSS的情况下也容易阅读，便于用户阅读和理解</li>
</ul>
<h3 id="三、如何“正确”的进行语义化"><a href="#三、如何“正确”的进行语义化" class="headerlink" title="三、如何“正确”的进行语义化"></a>三、如何“正确”的进行语义化</h3><p>前面提到，语义化是好的。但winter老师在专栏中也提到了，对于语义标签来说：“用对”比“不用”好，“不用”比“用错”好。因此如何正确的使用语义标签，是我们进行web语义化的重中之重。下面是winter老师所提到的比较重要的语义标签的使用场景。</p>
<h4 id="3-1-作为自然语言延伸的语义类的标签"><a href="#3-1-作为自然语言延伸的语义类的标签" class="headerlink" title="3.1 作为自然语言延伸的语义类的标签"></a>3.1 作为自然语言延伸的语义类的标签</h4><p>所谓的作为自然语言延伸的语义类的标签，就是为自然语言和纯文本的补充，用来表达一定的结构或者消除歧义。比较常见的就是是em和strong这两者了。我们可以通过使用类似的标签来消除一些歧义，让读者与机器都能更为准确的把握我们一句话的意思。至于具体的区别可以看下面这篇文章，作者对em和strong的区别做了一个较为全面和详细的探讨：</p>
<p><a href="https://segmentfault.com/a/1190000002481725" target="_blank" rel="noopener">em和strong的区别</a></p>
<h4 id="3-2-作为标题摘要的语义类标签"><a href="#3-2-作为标题摘要的语义类标签" class="headerlink" title="3.2 作为标题摘要的语义类标签"></a>3.2 作为标题摘要的语义类标签</h4><p>此类标签的用途是用于表现文章的结构，让文章的目录结构显得更加清晰有序。在HTML里，我们通常使用h1-h6来作为最基本的标题，而当我们需要副标题的时候，我们就需要使用hgroup，以免让副标题也产生一个层级。</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">hgroup</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">h1</span>&gt;</span>这是一个主标题<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">h2</span>&gt;</span>这是一个副标题<span class="tag">&lt;/<span class="name">h2</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">hgroup</span>&gt;</span></span><br></pre></td></tr></table></figure>
<p>此外在HTML5也提供了section标签，它会改变 h1-h6 的语义。section 的嵌套会使得其中的 h1-h6 下降一级。</p>
<h4 id="3-3-作为整体结构的语义类标签"><a href="#3-3-作为整体结构的语义类标签" class="headerlink" title="3.3 作为整体结构的语义类标签"></a>3.3 作为整体结构的语义类标签</h4><p>最后一个比较常见的场景就是很多浏览器所推出的“阅读模式”，以及各种非浏览器终端的出现，这让web语义化变得越来越重要。</p>
<p>而应用语义化标签来表现网页的结构，可以明确的表现出页面信息的主次关系，从而能更为精确的表现出阅读视图功能。也能让SEO更好。</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">header</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">nav</span>&gt;</span></span><br><span class="line">          ……</span><br><span class="line">        <span class="tag">&lt;/<span class="name">nav</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">header</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">aside</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">nav</span>&gt;</span></span><br><span class="line">        ……</span><br><span class="line">        <span class="tag">&lt;/<span class="name">nav</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">aside</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">section</span>&gt;</span>……<span class="tag">&lt;/<span class="name">section</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">section</span>&gt;</span>……<span class="tag">&lt;/<span class="name">section</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">section</span>&gt;</span>……<span class="tag">&lt;/<span class="name">section</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">footer</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">address</span>&gt;</span>……<span class="tag">&lt;/<span class="name">address</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">footer</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br></pre></td></tr></table></figure>
<p>上面这一块HTML就是一个语义化标签构成的body。其中 header 元素<br>代表“网页”或者“section”的页眉，通常包含h1-h6 元素或者 hgroup, 作为整个页面或者某个内容块的标题。也可以包裹一小节的目录，一个搜索框，一个nav等。需要注意的是：</p>
<ul>
<li>没有数量的限制</li>
<li>可以使“网页”或者任意“section”的头部部分</li>
</ul>
<p>至于nav元素则代表页面的导航链接区域，用于定义页面的<strong>主要导航部分</strong>。</p>
<p>aside 元素经常被包含在article元素中，作为主要内容的附属信息部分，其中的内容可以是与当前文章有关的相关资料，标签，名词解释等。<br>在article元素之外使用作为页面或站点全局的附属信息部分。最典型的是侧边栏，其中的内容可以是日志串连，其他组的导航，甚至广告，这些内容相关的页面。值得注意的是，aside 很容易被理解为侧边栏，实际上二者是包含关系，侧边栏是 aside ， aside 不一定是侧边栏。aside 和 header 中都可能出现导航（ nav 标签），二者的区别是， header 中的导航多数是到文章自己的目录，而 aside 中的导航多数是到关联页面或者是整站地图。</p>
<p> footer元素代表“网页”或任意“section”的页脚，通常含有该节的一些基本信息，譬如：作者，相关文档链接，版权资料。如果footer元素包含了整个节，那么它们就代表附录，索引，提拔，许可协议，标签，类别等一些其他类似信息。在这里它包含了 address ，这也是个非常容易被误用的标签。 这里的address 并非像 date 一样，表示一个给机器阅读的地址，而是表示 “ 文章（作者）的联系方式 ” ， address 明确地只关联到 article 和 body 。</p>
<p>除此之外，还有 article ，article 代表一个在文档，页面或者网站中自成一体的内容，其目的是为了让开发者独立开发或重用。所以， article 和 body 具有相似的结构，除了它的内容，article通常也会有一个标题(通常会在header里)，一个footer页脚等。同时，一个 HTML 页面中，可能有多个 article 存在。<br>一个典型的场景是多篇新闻展示在同一个新闻专题页面中，这种类似报纸的多文章结构适合用 article 来组织。</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">article</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">header</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">h1</span>&gt;</span>web 语义化的那些事儿<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">p</span>&gt;</span><span class="tag">&lt;<span class="name">time</span> <span class="attr">pubdate</span> <span class="attr">datetime</span>=<span class="string">"2018-03-23"</span>&gt;</span>2019-02-15<span class="tag">&lt;/<span class="name">time</span>&gt;</span><span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">header</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">p</span>&gt;</span>文章内容..<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">article</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">h2</span>&gt;</span>评论<span class="tag">&lt;/<span class="name">h2</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">article</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">header</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">h3</span>&gt;</span>评论者: 太平洋水军<span class="tag">&lt;/<span class="name">h3</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">header</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">p</span>&gt;</span>楼下真帅<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">article</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="tag">&lt;<span class="name">article</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">header</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">h3</span>&gt;</span>评论者: 黄海水军<span class="tag">&lt;/<span class="name">h3</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">header</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">p</span>&gt;</span>楼上长得帅<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">article</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">article</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">article</span>&gt;</span></span><br></pre></td></tr></table></figure>
<p>参考链接：</p>
<ul>
<li><a href="https://segmentfault.com/a/1190000002481725" target="_blank" rel="noopener">https://segmentfault.com/a/1190000002481725</a></li>
<li><a href="https://www.zhihu.com/question/20455165" target="_blank" rel="noopener">https://www.zhihu.com/question/20455165</a></li>
<li><a href="https://time.geekbang.org/column/intro/154" target="_blank" rel="noopener">https://time.geekbang.org/column/intro/154</a></li>
</ul>

          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://srtian96.gitee.io/blog/blog/2019/01/15/Three.js几何体的那些事儿/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="Srtian">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/blog/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Srtian'Blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/blog/2019/01/15/Three.js几何体的那些事儿/" itemprop="url">Three.js几何体的那些事儿</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2019-01-15T20:57:06+08:00">
                2019-01-15
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/Three-js/" itemprop="url" rel="index">
                    <span itemprop="name">Three.js</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          
            <div class="post-wordcount">
              
                
                <span class="post-meta-item-icon">
                  <i class="fa fa-file-word-o"></i>
                </span>
                
                  <span class="post-meta-item-text">字数统计&#58;</span>
                
                <span title="字数统计">
                  1,702
                </span>
              

              
                <span class="post-meta-divider">|</span>
              

              
                <span class="post-meta-item-icon">
                  <i class="fa fa-clock-o"></i>
                </span>
                
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                
                <span title="阅读时长">
                  7
                </span>
              
            </div>
          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <h3 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h3><p>前段时间将《Three.js入门指南》匆匆的翻了一翻，算是对Three.js有了一个较为基本的印象，但脑海中对其中的一些概念还是没有什么理解，因此决定对这些知识都进行一个较为深入的理解与学习。首先便从几何体的基本构成开始吧。<br>下面就是关于几何体对象的思维导图（如有错误敬请指正！）：</p>
<p><img src="https://images.gitee.com/uploads/images/2019/0115/202632_47506164_1575229.png" alt="输入图片说明" title="几何体对象.png"></p>
<h3 id="一、几何体对象概述"><a href="#一、几何体对象概述" class="headerlink" title="一、几何体对象概述"></a>一、几何体对象概述</h3><p>在Three.js中，我们如果可以通过各式各样的API来构建一个几何体，下面是部分构建几何体的API，我们可以通过官方文档来进行查阅：</p>
<blockquote>
<p><a href="https://threejs.org/docs/index.html#manual/en/introduction/Creating-a-scene" target="_blank" rel="noopener">https://threejs.org/docs/index.html#manual/en/introduction/Creating-a-scene</a></p>
<ul>
<li>BoxBufferGeometry</li>
<li>BoxGeometry</li>
<li>CircleBufferGeometry</li>
<li>CircleGeometry</li>
<li>ConeBufferGeometry</li>
<li>ConeGeometry</li>
<li>CylinderBufferGeometry</li>
<li>CylinderGeometry</li>
<li>…</li>
</ul>
</blockquote>
<p>很明显，如果使用There.js所提供的API构建集合体是非常方便的，比如这样：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> geometry = <span class="keyword">new</span> THREE.BoxGeometry(<span class="number">100</span>, <span class="number">100</span>, <span class="number">100</span>);</span><br></pre></td></tr></table></figure>
<p>上面的代码就可以让我们生成一个长、宽、高都为100的长方体，我们在后面所需做的就是将材质、灯光等属性设置完毕即可。这虽然简单便捷，但本质上就是一个黑盒，我们如果不加以深究，就只能在Three.js所封装好的API的基础进行开发，一旦有Three.js所提供的API所不能完成的需求出现时，就很可能无能为力。此外，如果对这些几何体的构成如果进行一些了解，也能有助于我们理解其几何体的本质。</p>
<p>通过上面那些封装好的对象的名字我们不难发现，大致的几何体类型有两大类：</p>
<ol>
<li>BufferGeometry</li>
<li>Geometry</li>
</ol>
<p>这两者的区别就在于 BufferGeometry 顶点数据是使用类型化数组来表示的，而Geometry的顶点数据则是用对象来表示的。</p>
<h3 id="二、BufferGeometry"><a href="#二、BufferGeometry" class="headerlink" title="二、BufferGeometry"></a>二、BufferGeometry</h3><p>当我们使用 BufferGeometry 时，我们可以这么来声明几何体顶点数据：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> geometry = <span class="keyword">new</span> THREE.BufferGeometry()</span><br><span class="line"><span class="comment">// 使用类型数组来创建顶点数据</span></span><br><span class="line"><span class="keyword">const</span> vertices = <span class="keyword">new</span> <span class="built_in">Float32Array</span>([</span><br><span class="line">  <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="comment">// 顶点1的坐标，下面分别是顶点2,3,4,5,6的坐标</span></span><br><span class="line">  <span class="number">100</span>, <span class="number">0</span>, <span class="number">0</span>, </span><br><span class="line">  <span class="number">0</span>, <span class="number">100</span>, <span class="number">0</span>, </span><br><span class="line">  <span class="number">0</span>, <span class="number">0</span>, <span class="number">100</span>, </span><br><span class="line">  <span class="number">0</span>, <span class="number">0</span>, <span class="number">100</span>, </span><br><span class="line">  <span class="number">100</span>, <span class="number">0</span>, <span class="number">10</span>, </span><br><span class="line">])</span><br><span class="line"><span class="comment">// 创建属性缓冲区对象，每3个为一组，分别表示一个顶点的xyz坐标</span></span><br><span class="line"><span class="keyword">const</span> attribue = <span class="keyword">new</span> THREE.BufferAttribute(vertices, <span class="number">3</span>)</span><br><span class="line"><span class="comment">// 设置几何体attributes属性的位置属性</span></span><br><span class="line">geometry.attributes.position = attribue</span><br></pre></td></tr></table></figure></p>
<p>通过上面的代码我们就能够声明几何体对象顶点的位置属性了，同样的我们也能设置颜色属性：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> colors = <span class="keyword">new</span> <span class="built_in">Float32Array</span>([</span><br><span class="line">  <span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>,</span><br><span class="line">  <span class="number">1</span>, <span class="number">0</span>, <span class="number">0</span>,</span><br><span class="line">  <span class="number">0</span>, <span class="number">0</span>, <span class="number">1</span>,</span><br><span class="line">  <span class="number">0</span>, <span class="number">1</span>, <span class="number">1</span>,</span><br><span class="line">  <span class="number">1</span>, <span class="number">1</span>, <span class="number">0</span>,</span><br><span class="line">  <span class="number">1</span>, <span class="number">0</span>, <span class="number">1</span>,</span><br><span class="line">])</span><br><span class="line"><span class="comment">// 设置几何体attributes属性的颜色color属性，三个为一组</span></span><br><span class="line">geometry.attributes.color = <span class="keyword">new</span> THREE.BufferAttribute(colors, <span class="number">3</span>)</span><br></pre></td></tr></table></figure>
<p>同样的法向量也是如此：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> normals = <span class="keyword">new</span> <span class="built_in">Float32Array</span>([</span><br><span class="line">  <span class="number">0</span>, <span class="number">0</span>, <span class="number">1</span>,</span><br><span class="line">  <span class="number">0</span>, <span class="number">0</span>, <span class="number">1</span>,</span><br><span class="line">  <span class="number">0</span>, <span class="number">0</span>, <span class="number">1</span>,</span><br><span class="line">  <span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>,</span><br><span class="line">  <span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>,</span><br><span class="line">  <span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>,</span><br><span class="line">])</span><br><span class="line"><span class="comment">// 设置几何体attributes属性的位置normal属性。三个为一组</span></span><br><span class="line">geometry.attributes.normal = <span class="keyword">new</span> THREE.BufferAttribute(normals, <span class="number">3</span>)</span><br></pre></td></tr></table></figure>
<p>通过上面的示例我们不难看出，当我们使用BufferGeometry来声明一个几何体对象时，我们可以通过一个个类型化的数据来声明顶点的具体情况，具体情况可参阅官方文档：</p>
<blockquote>
<p><a href="https://threejs.org/docs/index.html#api/en/core/BufferGeometry" target="_blank" rel="noopener">https://threejs.org/docs/index.html#api/en/core/BufferGeometry</a></p>
</blockquote>
<p>当然，这种申明方式既有优点又有缺点，优点就是实现了高度的可定制化，可以手动的设置每个顶点的属性，而缺点就在于一是手动设置麻烦，而是如果顶点过多，每个顶点都进行渲染的话会造成性能上的浪费，毕竟在绘制大的图形时，必定会有不少的顶点是重合的。为了减少这些缺点给项目带来的负面影响，Three.js又为我们提供了indedx。下面是官方文档对于index的描述：</p>
<blockquote>
<p>.index : （BufferAttribute）<br>Allows for vertices to be re-used across multiple triangles; this is called using “indexed triangles” and works much the same as it does in Geometry: each triangle is associated with the indices of three vertices. This attribute therefore stores the index of each vertex for each triangular face. If this attribute is not set, the renderer assumes that each three contiguous positions represent a single triangle. Default is null.</p>
</blockquote>
<p>具体的使用情况如下：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> indexes = <span class="keyword">new</span> <span class="built_in">Uint16Array</span>([</span><br><span class="line">  <span class="number">0</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>,</span><br><span class="line">])</span><br><span class="line"><span class="comment">// 设置为一个为一组，则0代表上面属性的第一组</span></span><br><span class="line">geometry.index = <span class="keyword">new</span> THREE.BufferAttribute(indexes, <span class="number">1</span>)</span><br></pre></td></tr></table></figure>
<h3 id="三、Geometry"><a href="#三、Geometry" class="headerlink" title="三、Geometry"></a>三、Geometry</h3><p>与BufferGeometry不同的是，Geometry是使用对象来描述顶点数据的就像这样：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> geometry = <span class="keyword">new</span> THREE.Geometry()</span><br><span class="line"><span class="comment">// 使用Vector3向量对象来表示顶点位置数据</span></span><br><span class="line"><span class="keyword">const</span> p1 = <span class="keyword">new</span> THREE.Vector3(<span class="number">50</span>, <span class="number">60</span>, <span class="number">0</span>)</span><br><span class="line"><span class="keyword">const</span> p2 = <span class="keyword">new</span> THREE.Vector3(<span class="number">0</span>, <span class="number">70</span>, <span class="number">20</span>)</span><br><span class="line"><span class="keyword">const</span> p3 = <span class="keyword">new</span> THREE.Vector3(<span class="number">80</span>, <span class="number">70</span>, <span class="number">0</span>)</span><br><span class="line"><span class="comment">// vertices是一个数组，因此我们只需将顶点push进去即可</span></span><br><span class="line">geometry.vertices.push(p1, p2, p3)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用Color对象来表示顶点颜色数据</span></span><br><span class="line"><span class="keyword">const</span> color1 = <span class="keyword">new</span> THREE.Color(<span class="number">0x00ff00</span>)</span><br><span class="line"><span class="keyword">const</span> color2 = <span class="keyword">new</span> THREE.Color(<span class="number">0x0000ff</span>)</span><br><span class="line"><span class="keyword">const</span> color3 = <span class="keyword">new</span> THREE.Color(<span class="number">0xff0000</span>)</span><br><span class="line"><span class="comment">// 将顶点颜色数据添加到geometry对象</span></span><br><span class="line">geometry.colors.push(color1, color2, color3)</span><br></pre></td></tr></table></figure></p>
<p>这样是不是很简单呀，但需要注意的是，在Geometry这个积类中，顶点的颜色颜色属性只能作用于点材质与线材质的几何体，对mesh类型的几何体没有作用。因此当我们需要使用mesh类型的几何体时，我们就需要使用face3属性来帮我们实现颜色上的定义，此外face3的用法与BufferGeometry中的index也很相像，都运用了顶点复用的思想：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> geometry = <span class="keyword">new</span> THREE.Geometry()</span><br><span class="line"><span class="comment">// 使用Vector3向量对象来表示顶点位置数据</span></span><br><span class="line"><span class="keyword">const</span> p1 = <span class="keyword">new</span> THREE.Vector3(<span class="number">50</span>, <span class="number">60</span>, <span class="number">0</span>)</span><br><span class="line"><span class="keyword">const</span> p2 = <span class="keyword">new</span> THREE.Vector3(<span class="number">0</span>, <span class="number">70</span>, <span class="number">20</span>)</span><br><span class="line"><span class="keyword">const</span> p3 = <span class="keyword">new</span> THREE.Vector3(<span class="number">80</span>, <span class="number">70</span>, <span class="number">0</span>)</span><br><span class="line"><span class="comment">// vertices是一个数组，因此我们只需将顶点push进去即可</span></span><br><span class="line">geometry.vertices.push(p1, p2, p3)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用Color对象来表示顶点颜色数据</span></span><br><span class="line"><span class="keyword">const</span> color1 = <span class="keyword">new</span> THREE.Color(<span class="number">0x00ff00</span>)</span><br><span class="line"><span class="keyword">const</span> color2 = <span class="keyword">new</span> THREE.Color(<span class="number">0x0000ff</span>)</span><br><span class="line"><span class="keyword">const</span> color3 = <span class="keyword">new</span> THREE.Color(<span class="number">0xff0000</span>)</span><br><span class="line"><span class="comment">// 将顶点颜色数据添加到geometry对象</span></span><br><span class="line">geometry.colors.push(color1, color2, color3)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用Face3构造函数创建一个三角面</span></span><br><span class="line"><span class="keyword">const</span> face1 = <span class="keyword">new</span> THREE.Face3(<span class="number">0</span>, <span class="number">1</span>, <span class="number">2</span>);</span><br><span class="line"><span class="comment">//设置每个顶点的法向量</span></span><br><span class="line"><span class="keyword">const</span> n1 = <span class="keyword">new</span> THREE.Vector3(<span class="number">0</span>, <span class="number">0</span>, <span class="number">-1</span>);</span><br><span class="line"><span class="keyword">const</span> n2 = <span class="keyword">new</span> THREE.Vector3(<span class="number">0</span>, <span class="number">0</span>, <span class="number">-1</span>);</span><br><span class="line"><span class="keyword">const</span> n3 = <span class="keyword">new</span> THREE.Vector3(<span class="number">0</span>, <span class="number">0</span>, <span class="number">-1</span>);</span><br><span class="line"><span class="comment">// 等于这样的：face1.normal = new THREE.Vector3(0, 0, -1)</span></span><br><span class="line">face1.vertexNormals.push(n1, n2, n3);</span><br><span class="line"><span class="comment">// 设置三角面face1三个顶点的颜色</span></span><br><span class="line">face1.vertexColors = [</span><br><span class="line">  <span class="keyword">new</span> THREE.Color(<span class="number">0xffff00</span>),</span><br><span class="line">  <span class="keyword">new</span> THREE.Color(<span class="number">0xff00ff</span>),</span><br><span class="line">  <span class="keyword">new</span> THREE.Color(<span class="number">0x00ffff</span>),</span><br><span class="line">]</span><br><span class="line"><span class="comment">// 若颜色一样只需这么声明：face1.color = new THREE.Color(0x00ff00);</span></span><br><span class="line"></span><br><span class="line">geometry.faces.push(face1)</span><br></pre></td></tr></table></figure>

          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://srtian96.gitee.io/blog/blog/2018/12/29/《点石成金》读书笔记/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="Srtian">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/blog/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Srtian'Blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/blog/2018/12/29/《点石成金》读书笔记/" itemprop="url">《点石成金》</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-12-29T21:14:23+08:00">
                2018-12-29
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/设计/" itemprop="url" rel="index">
                    <span itemprop="name">设计</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          
            <div class="post-wordcount">
              
                
                <span class="post-meta-item-icon">
                  <i class="fa fa-file-word-o"></i>
                </span>
                
                  <span class="post-meta-item-text">字数统计&#58;</span>
                
                <span title="字数统计">
                  1,906
                </span>
              

              
                <span class="post-meta-divider">|</span>
              

              
                <span class="post-meta-item-icon">
                  <i class="fa fa-clock-o"></i>
                </span>
                
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                
                <span title="阅读时长">
                  6
                </span>
              
            </div>
          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <h3 id="一、别让我思考"><a href="#一、别让我思考" class="headerlink" title="一、别让我思考"></a>一、别让我思考</h3><p>网页的易用性最重要的一点就是无需用户去思考。这也代表着交互一般劲量趋向于简单化，平整化，以减少用户的认知负担，减少对用户的干扰。</p>
<p>示例：亚马逊搜索没有提及一定要去搜索什么内容，只会对你所输入的进行分析，这样就会减少用户的思考的成本（淘宝也是如此，通过自然语言处理来对用户的输入进行分析）</p>
<p>比较典型的地方：</p>
<ul>
<li>按钮是否明显</li>
<li>相同的信息是否集中</li>
<li>搜索是否便捷</li>
<li>关键功能的位置是否显眼、是否易用</li>
</ul>
<h3 id="二、扫描，满意即可，勉强应付"><a href="#二、扫描，满意即可，勉强应付" class="headerlink" title="二、扫描，满意即可，勉强应付"></a>二、扫描，满意即可，勉强应付</h3><p>根据我自身使用web的时候，突然觉得这个很有道理。往往当我们在web上进行浏览的时候，只会对我们所关注的信息进行仔细的观察。也就是说，我们在访问一个网页时，会首先对整个网页进行一次扫描，在这个扫描的过程中，我们会对我们感兴趣的知识进行提取，而那些我们在扫描过程中不是很在意的东西就会进行忽略。</p>
<p>为什么扫描：</p>
<ul>
<li>我们总是处于忙碌中</li>
<li>我们知道自己不必阅读所有内容</li>
<li>我们善于扫描</li>
</ul>
<p>也真是由于用户只是扫描页面，因此其实用户并不会去真正的寻找最佳结构，而是满意即可，即选择第一个合理的选项。（我们在作出决策也是如此，只会寻找第一个合理的决策就可能去执行）</p>
<h3 id="三、-广告牌设计101法则（为扫描设计，不为阅读设计）"><a href="#三、-广告牌设计101法则（为扫描设计，不为阅读设计）" class="headerlink" title="三、 广告牌设计101法则（为扫描设计，不为阅读设计）"></a>三、 广告牌设计101法则（为扫描设计，不为阅读设计）</h3><p>正是由于我们是扫描页面的，因此要采取以下五个原则：</p>
<ol>
<li>在每个页面上建立清楚的视觉层次</li>
<li>尽量利用习惯用法</li>
<li>把页面划分成明确定义的区域</li>
<li>明显表示可以点击的地方</li>
<li>最大限度降低干扰</li>
</ol>
<h4 id="3-1-建立清楚的视觉层次"><a href="#3-1-建立清楚的视觉层次" class="headerlink" title="3.1 建立清楚的视觉层次"></a>3.1 建立清楚的视觉层次</h4><p>一个视觉层次清楚的页面有三个特点：</p>
<ol>
<li>越重要的部分越突出（h1-h6/p1-p6）</li>
<li>逻辑上相关的部分在视觉上也相关</li>
<li>逻辑上包含的部分在视觉上进行嵌套</li>
</ol>
<h4 id="3-2-关于习惯用法"><a href="#3-2-关于习惯用法" class="headerlink" title="3.2 关于习惯用法"></a>3.2 关于习惯用法</h4><p>首先他们很重要，正是由于习惯、所以符合用户的习惯。但由于设计师们需要造轮子来证明自己（其实工程师也是这样），所以他们一般不想直接使用习惯用法，而是设计出属于自己的一套方法论。</p>
<h4 id="3-3-把页面划分为明确定义的区域"><a href="#3-3-把页面划分为明确定义的区域" class="headerlink" title="3.3 把页面划分为明确定义的区域"></a>3.3 把页面划分为明确定义的区域</h4><p>这可以让用户快速决定关注页面的那些位置（即重点扫描的区域）</p>
<h4 id="3-4-明显表示可以点击的地方"><a href="#3-4-明显表示可以点击的地方" class="headerlink" title="3.4 明显表示可以点击的地方"></a>3.4 明显表示可以点击的地方</h4><p>颜色不同、质感不同等方式。别让用户到处去找</p>
<h4 id="3-5-降低视觉噪声"><a href="#3-5-降低视觉噪声" class="headerlink" title="3.5 降低视觉噪声"></a>3.5 降低视觉噪声</h4><p>页面的难以理解最大的原因就在于视觉噪声，视觉噪声大致分为两大类：</p>
<ul>
<li>眼花缭乱：满眼惊叹号，毫无层次等</li>
<li>背景噪声</li>
</ul>
<h3 id="第四章-为什么用户喜欢无需思考的选择"><a href="#第四章-为什么用户喜欢无需思考的选择" class="headerlink" title="第四章 为什么用户喜欢无需思考的选择"></a>第四章 为什么用户喜欢无需思考的选择</h3><p>一般来说用户在达到目标前所需要的点击次数不应过多（很多网站规定点击次数不应该超过5次）（通常来说网页越平整化越好）</p>
<blockquote>
<p>注：三次无需思考，明确无误的点击相当与一次需要思考的地点击</p>
</blockquote>
<h3 id="第五章-省略不必要的文字"><a href="#第五章-省略不必要的文字" class="headerlink" title="第五章 省略不必要的文字"></a>第五章 省略不必要的文字</h3><blockquote>
<p>去掉每个页面上一半的文字，然后把剩下的文字再去掉一般 —— krug可用性第三定律</p>
</blockquote>
<p>去掉多余文字的好处：</p>
<ul>
<li>可以降低页面的噪声</li>
<li>让有用的内容更突出</li>
<li>让页面更尖端，让用户在浏览时可以扫描的内容更多，无需滚屏</li>
</ul>
<p>那么我们可以做下面一些事情：</p>
<ol>
<li>欢迎词必须消灭</li>
<li>知识说明必须要消灭</li>
</ol>
<h3 id="第六章-设计导航"><a href="#第六章-设计导航" class="headerlink" title="第六章 设计导航"></a>第六章 设计导航</h3><p>如果网页上让大家找不到方向，人们就不会使用你的网站。</p>
<p>那么重要的事情首先要做的事就是让相关的事物集中在一块，这样当用户在浏览网页的时候，就能快速定位自己所需要找的内容的大体位置。</p>
<h4 id="6-1-网络导航101法则"><a href="#6-1-网络导航101法则" class="headerlink" title="6.1 网络导航101法则"></a>6.1 网络导航101法则</h4><p>我们浏览一个网页的过程：</p>
<ul>
<li>你通常是为了寻找某个目标</li>
<li>如果选择浏览，你将通过标志的引导在层次结构中穿行</li>
<li>找不到我们就会跑路</li>
</ul>
<p>导航的作用：</p>
<ol>
<li>他给了我们一些固定的感觉</li>
<li>他告诉我们当前的位置</li>
<li>他告诉我们如何使用网站</li>
<li>他给了我们对网站建造者的信心</li>
</ol>
<p>重要的是由这两者元素：主页和表单</p>
<ul>
<li>主页：我们可以直接通过主页来确定我们当前的位置</li>
<li>表单：可以将栏目、工具都汇聚在这里，但依然要注意相关性的原则，且层级不应该过深（尽量少于3次）</li>
</ul>
<p>此外要注意的是要保证，无论用户到哪都给他一个可以直接返回主页的按钮，这可以给用户安全感，不会迷失方向。</p>
<p>此外我们还有以下几点需要避免的：</p>
<ul>
<li>花哨的用词</li>
<li>指示说明</li>
<li>选项</li>
</ul>
<p>页面名称需要注意的：</p>
<ul>
<li>大小应该要大于其他的文字</li>
<li>应该出现在合适的位置</li>
<li>每个页面都需要一个名称</li>
<li>名称要引人注意</li>
<li>名称要和点击的链接一致</li>
</ul>
<h4 id="6-2-层级导航"><a href="#6-2-层级导航" class="headerlink" title="6.2 层级导航"></a>6.2 层级导航</h4><p>除了主页面的导航外，还有就是使用层级导航来帮助用户导航。关于层级导航有以下几点最佳实践：</p>
<ul>
<li>把他们放在最顶端</li>
<li>使用&gt;对层级进行分隔</li>
<li>使用小字体</li>
<li>使用了文字“你在这里”</li>
<li>将最后一个元素加粗</li>
<li>没有把他们用作页面的名称</li>
</ul>
<h4 id="6-3-标签"><a href="#6-3-标签" class="headerlink" title="6.3 标签"></a>6.3 标签</h4><p>标签的好处：</p>
<ol>
<li>它们不言而喻</li>
<li>它们很难错过</li>
<li>它们很灵活</li>
<li>它们暗示了一个物理空间</li>
</ol>
<p>标签的绘制方法：</p>
<ol>
<li>正确绘制</li>
<li>颜色编码（选中的与没选中的不一样）</li>
<li>当你进入网站时，有一个标签已经选中</li>
</ol>
<h4 id="6-4-后备箱测试"><a href="#6-4-后备箱测试" class="headerlink" title="6.4 后备箱测试"></a>6.4 后备箱测试</h4><p>所谓的后备箱测试指的是页面设计良好，我们就能快速的答出以下的一些问题：</p>
<ul>
<li>这是什么网站</li>
<li>我在哪个网页上</li>
<li>这个网站的主要栏目是哪些</li>
<li>在这个层次上我有哪些选择</li>
<li>我在导航系统的什么位置</li>
<li>我怎么搜索</li>
</ul>
<p><strong>降低用户好感的方式：</strong></p>
<ul>
<li>隐藏我想要的信息</li>
<li>因为没有按照你们的方式行事而惩罚我</li>
<li>向我询问不必要的信息</li>
<li>敷衍我，欺骗我</li>
<li>给我设置障碍</li>
<li>你的网站看起来不专业</li>
</ul>
<p><strong>提高好感的几种方式：</strong></p>
<ul>
<li>知道人们在你网站上像做什么，并让他们明白简易</li>
<li>告诉我我想知道的</li>
<li>尽量减少步骤</li>
<li>花点心思</li>
<li>知道我可能有哪些疑问，并且给予解答</li>
<li>为我提供协助</li>
<li>容易从错误中恢复</li>
<li>如有不确定，记得道歉</li>
</ul>

          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://srtian96.gitee.io/blog/blog/2018/12/17/关于2018，我有这些想谈谈的/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="Srtian">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/blog/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Srtian'Blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/blog/2018/12/17/关于2018，我有这些想谈谈的/" itemprop="url">关于2018，我有这些想谈谈的</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-12-17T23:31:35+08:00">
                2018-12-17
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/随笔/" itemprop="url" rel="index">
                    <span itemprop="name">随笔</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          
            <div class="post-wordcount">
              
                
                <span class="post-meta-item-icon">
                  <i class="fa fa-file-word-o"></i>
                </span>
                
                  <span class="post-meta-item-text">字数统计&#58;</span>
                
                <span title="字数统计">
                  1,648
                </span>
              

              
                <span class="post-meta-divider">|</span>
              

              
                <span class="post-meta-item-icon">
                  <i class="fa fa-clock-o"></i>
                </span>
                
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                
                <span title="阅读时长">
                  6
                </span>
              
            </div>
          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <h3 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h3><p>前面一段时间看了一篇文章，有提到每过一段时间就要对自己进行一个总结，这样才能更好地总结过去拥抱未来。我觉得很有道理，因此决定往后每年都进行一次年终总结，一是总结自己一年中的成长与得失，二是对后面的一年进行期望与规划。</p>
<h3 id="关于2018年"><a href="#关于2018年" class="headerlink" title="关于2018年"></a>关于2018年</h3><h4 id="1-技术方面"><a href="#1-技术方面" class="headerlink" title="1. 技术方面"></a>1. 技术方面</h4><p>算是打好了JavaScript的基础，并对react也进行了一个较为深入的学习，但这两者都还是需要继续努力。此外也对Angular进行了一些了解，算是对三大框架都进行了一个了解，也尝试着从开发需求的角度去思考这三大框架各自适应的开发场景，算是让自己在看待技术上转变了以往的一些思维。而在秋招后，又对Three.js进行了一些学习，看完了《Three.js入门指南》，感觉挺有意思的，算是又找到了自己感兴趣的一个技术方向。</p>
<h4 id="2-博客"><a href="#2-博客" class="headerlink" title="2.博客"></a>2.博客</h4><p>2018年算是坚持每个月都进行博客输出，总结自己在一段时间下的学习的心得与体会，自己也算是尝到了写博客带来的益处，也收获了一些小的成绩：</p>
<ul>
<li>博客的访问量上千了，并且2018年总共码了9万余字：<a href="https://srtian96.gitee.io/blog/">https://srtian96.gitee.io/blog/</a></li>
<li>掘金：<br><img src="https://images.gitee.com/uploads/images/2018/1214/225238_8c7c3e13_1575229.jpeg" alt="输入图片说明" title="Screenshot_20181214-224713.jpg"></li>
<li>简书（话说关于koa的文章好像被培训机构拿来给学生看了，阅读量蜜汁上万了）：<br><img src="https://images.gitee.com/uploads/images/2018/1214/225349_ffec87cc_1575229.jpeg" alt="输入图片说明" title="Screenshot_20181214-224842.jpg"></li>
</ul>
<h4 id="3-秋招"><a href="#3-秋招" class="headerlink" title="3. 秋招"></a>3. 秋招</h4><p>秋招对于我来说算是跌宕起伏，起初抱着很低的期望去参加秋招，想着能找个10K以上的工作就不错了。但在有意思的是最初就面了阿里，成功了完成了拿阿里练手的成就（皮一下），并很幸运的走完了流程。虽然由于各种原因没能成功，但也算建立了自信。但随后又经历了美团、百度等大厂提前批的各种挂，很郁闷。所幸到了10月份，运气好了起来，面的公司基本上都过了，最后也收获了几个offer，其中也有些不错的，最终也选择了一个自己感觉很有前景很厉害的公司，在技术栈方面也是想给自己未来多一些挑战，脱离舒适区。但话虽然是这样说，但对于未来内心还是有些忐忑，加油吧~</p>
<p>此外，通过与诸多的面试官进行交流，也有了以下的一些思考：</p>
<ul>
<li>在看待框架或一些工具的时候，可以去思考我们所需要的应用场景是怎样的，以及提前去思考当项目复杂后的通信问题、工程问题、以及可维护的问题。</li>
<li>可以去关注关注浏览器渲染性能的问题（按照阿里的那位师兄描述大部分问题都集中在W3C标准和HTTP标准中），因此不仅仅要知道怎么做，还需要去思考为什么要这样做。</li>
<li>多去思考一些栈外的东西，培养端对端的思维，建立全局意识。比如前后端的边界问题、运维、产品、设计等等。只有这样才能遇到一些需求，能够以较为全面的眼光去思考解决方案。</li>
<li>前端的未来会因为硬件与网络的提升而发生改变，未来的前端不应该只局限于写个网页这么简单，比如计算的前置、物联网、可视化，都是未来可期的东西。（所以加紧学习呀）</li>
</ul>
<h4 id="4-旅行与摄影"><a href="#4-旅行与摄影" class="headerlink" title="4. 旅行与摄影"></a>4. 旅行与摄影</h4><p>说来惭愧啊，在这两方面并没有太多可说的，2018年只去了杭州和北京，没有去什么地方，整整一年都在忙碌着，但回头看来效率却并不高。而相机一直也放在家里，没有带到学校来，因此拍的照片也数量有限，只是在一些摄影的思路上有了一些新的认识。因此在这两方面，在2019都需要做的更好才行~</p>
<h4 id="5-阅读"><a href="#5-阅读" class="headerlink" title="5. 阅读"></a>5. 阅读</h4><p>在看书方面，所看的书也是有限而分散的，并没有太多系统的阅读其他的课外书籍（也就是非技术书籍），零零散散的阅读量可能没上十本，十分惭愧，较之2017年的20来本差距有点大，在2019年必须得有所改善才行。</p>
<h3 id="对于2019年"><a href="#对于2019年" class="headerlink" title="对于2019年"></a>对于2019年</h3><h4 id="关于技术的一些期望"><a href="#关于技术的一些期望" class="headerlink" title="关于技术的一些期望"></a>关于技术的一些期望</h4><p>根据秋招得到的建议，与成为一个靠谱的合格的工程师的目标，对于技术方面我有以下的一些计划：</p>
<ul>
<li>首先努力成为一名准全栈工程师，至少将前后端给打通。（后端主要学习Node.js与python）</li>
<li>主要技术可预见的还是JavaScript和react，对这两者要加深学习</li>
<li>对设计方面与产品方面初步建立自己的方法论与知识体系</li>
<li>学习一门新的语言，初步定为Go或Java，视具体情况而定。</li>
<li>掌握Three.js，并对webGL进行学习（并将相关的计算机图形学以及数学知识掌握）</li>
<li>学习Nginx，并对其进行实践与掌握</li>
<li>弥补CSS上的一些漏洞</li>
<li>健全自己前端的知识体系，打好打牢基础。<h4 id="博客"><a href="#博客" class="headerlink" title="博客"></a>博客</h4></li>
<li>坚持写博客，每月最少输出一篇博客。</li>
<li>尝试写一些影评或者读书笔记</li>
<li>去写游记，纪录一些自己觉得很不错的，很美的地方。</li>
<li>在工作后，做好每日的工作纪录，并及时总结与思考，所得到的心得体会也需要定期进行输出总结。</li>
</ul>
<h4 id="其他："><a href="#其他：" class="headerlink" title="其他："></a>其他：</h4><ul>
<li>旅行：每月出去短途旅行一次，去逛逛北京的一些景点也行，做到工作与生活的平衡，别宅着。（也希望出国旅行一次）</li>
<li>阅读：每个月坚持阅读一本好书，用心去读，做好读书笔记。</li>
<li>健身：每天做俯卧撑，工作后尽量找到合适的健身的方式，每星期至少健身3次，保持一个健康的身体。</li>
<li>好好学英语，做到无障碍的阅读英语文章、看英语视频、写英语的文档。（毕竟英语是工程师的生产力）</li>
<li>训练自己的专注力，让自己可以在一段时间内专注于一件事情，不为外界所打扰。</li>
</ul>

          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://srtian96.gitee.io/blog/blog/2018/12/09/JavaScript数据处理之数组操作/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="Srtian">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/blog/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Srtian'Blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/blog/2018/12/09/JavaScript数据处理之数组操作/" itemprop="url">JavaScript数据处理之数组操作</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-12-09T18:28:19+08:00">
                2018-12-09
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/JavaScript/" itemprop="url" rel="index">
                    <span itemprop="name">JavaScript</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          
            <div class="post-wordcount">
              
                
                <span class="post-meta-item-icon">
                  <i class="fa fa-file-word-o"></i>
                </span>
                
                  <span class="post-meta-item-text">字数统计&#58;</span>
                
                <span title="字数统计">
                  2,681
                </span>
              

              
                <span class="post-meta-divider">|</span>
              

              
                <span class="post-meta-item-icon">
                  <i class="fa fa-clock-o"></i>
                </span>
                
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                
                <span title="阅读时长">
                  11
                </span>
              
            </div>
          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <h2 id="一、概述"><a href="#一、概述" class="headerlink" title="一、概述"></a>一、概述</h2><p>在JavaScript中，数组是数据类型中非常重要的一环。所传入的数据一般也都需要转化为数组，然后通过数组所提供的API来对数据进行处理，当然它可能本身传来的就是数组。此外，在函数式编程中，数组也提供了不少的API来帮助我们实现函数式编程，从而使代码变得更为可读，因此对数组的一些操作进行一个总结还是很有必要的。</p>
<p>由于在JavaScript中，数组的API还是挺多的，因此本文将主要讨论一些常用的API：</p>
<ul>
<li>Array.join</li>
<li>Array.slice</li>
<li>Array.sort</li>
<li>Array.forEach</li>
<li>Array.map</li>
<li>Array.filter</li>
<li>Array.reduce</li>
</ul>
<p>其中Array.map、Array.filter、Array.reduce都是高阶函数。</p>
<h2 id="二、数组操作"><a href="#二、数组操作" class="headerlink" title="二、数组操作"></a>二、数组操作</h2><h3 id="2-1-Array-join"><a href="#2-1-Array-join" class="headerlink" title="2.1 Array.join()"></a>2.1 Array.join()</h3><p> Array.join()是个非常简单的API，该方法将一个数组（或一个类数组对象）的所有元素连接成一个字符串并返回这个字符串。具体用法如下：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> a = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]</span><br><span class="line">a.join(<span class="string">'-'</span>) <span class="comment">// "1-2-3"</span></span><br></pre></td></tr></table></figure></p>
<p>其中join的工作其实很简单：即遍历数组，并将数组中的每一个元素都与传入的参数相加。模拟起来大致的运行流程就是这样：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Array</span>.prototype.join = <span class="function"><span class="keyword">function</span>(<span class="params">char</span>)</span>&#123;</span><br><span class="line">  <span class="keyword">let</span> result = <span class="keyword">this</span>[<span class="number">0</span>] || <span class="string">''</span></span><br><span class="line">  <span class="keyword">let</span> len = <span class="keyword">this</span>.length</span><br><span class="line">  <span class="keyword">for</span>(<span class="keyword">let</span> i = <span class="number">1</span>; i &lt; len; i++)&#123;</span><br><span class="line">      result += char + <span class="keyword">this</span>[i]</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> result</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>此外，join()也能连接类数组：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">f</span>(<span class="params">a, b, c</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">var</span> s = <span class="built_in">Array</span>.prototype.join.call(<span class="built_in">arguments</span>);</span><br><span class="line">  <span class="built_in">console</span>.log(s);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">f(<span class="number">1</span>, <span class="string">'a'</span>, <span class="literal">true</span>)  <span class="comment">// '1,a,true'</span></span><br></pre></td></tr></table></figure>
<h3 id="2-2-Array-slice"><a href="#2-2-Array-slice" class="headerlink" title="2.2 Array.slice"></a>2.2 Array.slice</h3><p>Array.slice(beginIndex, endIndex) 方法返回一个新的数组对象，这一对象是一个由 begin 和 end（不包括end）决定的原数组的浅拷贝,且原始数组不会被改变：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> fruits = [<span class="string">'Banana'</span>, <span class="string">'Orange'</span>, <span class="string">'Lemon'</span>, <span class="string">'Apple'</span>, <span class="string">'Mango'</span>]</span><br><span class="line"><span class="keyword">let</span> citrus = fruits.slice(<span class="number">1</span>, <span class="number">3</span>) <span class="comment">// ['Orange','Lemon']</span></span><br></pre></td></tr></table></figure>
<p>它的用法也很简单，因此在ES6出现之前，很多人都用它来讲类数组装化为数组：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">array = <span class="built_in">Array</span>.prototye.slice.call(arrayLike)</span><br><span class="line"><span class="comment">// or</span></span><br><span class="line">array = [].slice.call(arrayLike)</span><br></pre></td></tr></table></figure></p>
<p>所以仔细想想Array.slice()的套路大概就是这样：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Array</span>.prototype.slice = <span class="function"><span class="keyword">function</span>(<span class="params">begin, end</span>)</span>&#123;</span><br><span class="line">    begin = begin || <span class="number">0</span></span><br><span class="line">    end = end || <span class="keyword">this</span>.length</span><br><span class="line">    <span class="keyword">let</span> result = []</span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">let</span> i = begin; i &lt; end; i++)&#123;</span><br><span class="line">        result.push(<span class="keyword">this</span>[i])</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> result</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>当然了，使用此方法来将类数组转化为数组非常的不优雅，因此ES6又提供了Array.from来讲类数组转化为数组：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> a = array.from(arrayLike)</span><br></pre></td></tr></table></figure>
<h3 id="2-3-Array-sort"><a href="#2-3-Array-sort" class="headerlink" title="2.3 Array.sort"></a>2.3 Array.sort</h3><p>Array.sort([callback])也是数组操作中常用的一个API，其内置的排序算法主要是快速排序和插入排序的混合，当数组长度低于11个的时候就是用插入排序，除此之外就使用快排。具体代码可以直接去看源码：</p>
<blockquote>
<p>github.com/v8/v8/blob/master/src/js/array.js#L718</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">InnerArraySort</span>(<span class="params">array, length, comparefn</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// In-place QuickSort algorithm.</span></span><br><span class="line">  <span class="comment">// For short (length &lt;= 10) arrays, insertion sort is used for efficiency.</span></span><br><span class="line">  <span class="keyword">if</span> (!IS_CALLABLE(comparefn)) &#123;</span><br><span class="line">    comparefn = <span class="function"><span class="keyword">function</span> (<span class="params">x, y</span>) </span>&#123;</span><br><span class="line">      <span class="keyword">if</span> (x === y) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">      <span class="keyword">if</span> (%_IsSmi(x) &amp;&amp; %_IsSmi(y)) &#123;</span><br><span class="line">        <span class="keyword">return</span> %SmiLexicographicCompare(x, y);</span><br><span class="line">      &#125;</span><br><span class="line">      x = TO_STRING(x);</span><br><span class="line">      y = TO_STRING(y);</span><br><span class="line">      <span class="keyword">if</span> (x == y) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">      <span class="keyword">else</span> <span class="keyword">return</span> x &lt; y ? <span class="number">-1</span> : <span class="number">1</span>;</span><br><span class="line">    &#125;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>注释也说的很清楚，默认使用快速排序，但是当数组长度小于11的数组，使用的是插入排序。这也符合我们的正常观点，因为插入排序在数组长度小于一定值的时候是会比快速排序速度更快，而快速排序在大规模数据的时候则更有优势。插入排序是稳定的，快速排序是不稳定的。当然这在不同的浏览器引擎中的表现是不同的，比如在火狐浏览器中使用的则是归并排序。</p>
<p>那么我们一般如何使用它呢？首先它的语法是这样的：Array.sort([callback])。也就是说我们可用通过定制callback也就是回调函数来定义如何对数组里的元素进行排序。但获取有些小老弟会发现sort有这么一个很有意思的特性，这也是我早期学JavaScript时，碰到的一个很有意思的问题：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> numbers1 = [<span class="number">1</span>, <span class="number">3</span>, <span class="number">5</span>, <span class="number">2</span>, <span class="number">4</span>]</span><br><span class="line">numbers1.sort()</span><br><span class="line"><span class="built_in">console</span>.log(numbers1) <span class="comment">// [1, 2, 3, 4, 5]</span></span><br><span class="line"><span class="comment">// 还有这样的</span></span><br><span class="line"><span class="keyword">let</span> numbers2 = [<span class="number">1</span>, <span class="number">15</span>, <span class="number">20</span>, <span class="number">4</span>, <span class="number">3</span>]</span><br><span class="line">numbers2.sort()</span><br><span class="line"><span class="built_in">console</span>.log(numbers2) <span class="comment">// [1, 15, 20, 3, 4]</span></span><br></pre></td></tr></table></figure>
<p>我们可以看到，同样是调用sort()方法，但上下两个数组所得到的结果完全不同。其实其根本原因在于sort方法执行的时候，数组的每个元素会先执行一次 toString() 方法，然后在根据字符串的 Unicode 编码进行排序。因此我们在使用sort的时候就需要定义相应的callback，来达到排序的目的，比如最常见的升序排序：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> a = [<span class="number">1</span>, <span class="number">23</span>, <span class="number">5</span>, <span class="number">12</span>, <span class="number">4</span>]</span><br><span class="line">a.sort(<span class="function">(<span class="params">a, b</span>) =&gt;</span> a - b)</span><br><span class="line">consloe.log(a) <span class="comment">// [1, 4, 5, 12, 23]</span></span><br></pre></td></tr></table></figure>
<p>其计算规则很简单：</p>
<ul>
<li>如果 a - b 小于 0 ，那么 a 在 b 的前面，也就是会按照升序排列</li>
<li>如果 a - b 等于 0 ，那么 a 和 b 的位置保持不变（相对的，视具体算法而变）</li>
<li>如果 a - b 大于 0 ，那么 b 在 a 的前面，也就是会按照降序排列</li>
</ul>
<p>当然我们也不局限如此，我们经常碰到的数据可能不局限于单纯的数字数组，比如我在一次面试时，面试官就给了我这么一串数据：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[&#123;<span class="attr">order</span>: <span class="number">3</span>&#125;,&#123;<span class="attr">order</span>: <span class="number">4</span>&#125;,&#123;<span class="attr">order</span>: <span class="number">2</span>&#125;,&#123;<span class="attr">order</span>: <span class="number">100</span>&#125;]</span><br></pre></td></tr></table></figure>
<p>让我将其根据order的值从大到小进行排序。既然而然我们就能使用sort来对其进行排序了：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> order = [&#123;<span class="attr">order</span>: <span class="number">3</span>&#125;,&#123;<span class="attr">order</span>: <span class="number">4</span>&#125;,&#123;<span class="attr">order</span>: <span class="number">2</span>&#125;,&#123;<span class="attr">order</span>: <span class="number">100</span>&#125;]</span><br><span class="line"><span class="keyword">var</span> result = order.sort(<span class="function">(<span class="params">a, b</span>) =&gt;</span> b.order - a.order)</span><br></pre></td></tr></table></figure>
<p>这样就能实现我们预期的效果了，是不是很强大、很简单~</p>
<h3 id="2-4-Array-forEach"><a href="#2-4-Array-forEach" class="headerlink" title="2.4 Array.forEach"></a>2.4 Array.forEach</h3><p>forEach这个API在我们日常开发中也很有用（当然其实我个人认为在map出来后可以使用map来代替forEach,这个在后面说为啥），它可以对数组的每一个元素都进行操作，比如这样：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> a = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]</span><br><span class="line">a.forEach(<span class="function">(<span class="params">item</span>) =&gt;</span> <span class="built_in">console</span>.log(item + <span class="number">1</span>))</span><br><span class="line"><span class="comment">// 2 3 4</span></span><br></pre></td></tr></table></figure></p>
<p>它的大致实现思路就像这样：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Array</span>.prototype.forEach = <span class="function"><span class="keyword">function</span>(<span class="params">fn</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>, len = <span class="keyword">this</span>.length; i &lt; len ; i++) &#123;</span><br><span class="line">         fn.apply(<span class="keyword">this</span>, [<span class="keyword">this</span>[i], i, <span class="keyword">this</span>])</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>因此总的来说，forEach与我们常用的for循环在功能上存在重叠，也在一定程度上可以替代for循环的一些功能，但需要注意的是，它与for循环还是存在以下一些差异：</p>
<ul>
<li>forEach 没法 break</li>
<li>forEach 用到了函数，因此每次迭代都会产生一个新的函数作用域；而 for 循环只有一个作用域</li>
</ul>
<h3 id="2-5-Array-map"><a href="#2-5-Array-map" class="headerlink" title="2.5 Array.map"></a>2.5 Array.map</h3><p>map()方法在功能上和forEach相差不大，但由于它本身存在返回值，且调用起来也更为方便，代码可读性也更高，因此推荐使用map：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> a = [<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>]</span><br><span class="line"><span class="keyword">let</span> b = a.map(<span class="function">(<span class="params">item</span>) =&gt;</span> item + <span class="number">1</span>)</span><br><span class="line"><span class="built_in">console</span>.log(b)</span><br></pre></td></tr></table></figure></p>
<p>同时，在函数式编程中，使用map保证存在一个返回值也是达到链式编程的一个重要的基础。map大概的代码逻辑应该是这样的：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Array</span>.prototype.map = <span class="function"><span class="keyword">function</span> (<span class="params">fn</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">let</span> result = [];</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>, len = <span class="keyword">this</span>.length; i &lt; len; i++) &#123;</span><br><span class="line">         result[i] = fn.apply(<span class="keyword">this</span>, [<span class="keyword">this</span>[i], i, <span class="keyword">this</span>]);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> result</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<h3 id="2-6-Array-filter"><a href="#2-6-Array-filter" class="headerlink" title="2.6 Array.filter"></a>2.6 Array.filter</h3><p>filter() 方法创建一个包含所有通过测试的元素的新数组。我们通常使用filter()来对数组进行筛选，挑选出我们需要的元素，同时也可以使用代码更具有可读性：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> prices = [<span class="number">25</span>, <span class="number">30</span>, <span class="number">15</span>, <span class="number">55</span>, <span class="number">40</span>, <span class="number">10</span>]</span><br><span class="line">prices.filter(<span class="function">(<span class="params">item</span>) =&gt;</span> item &gt;= <span class="number">30</span>)</span><br><span class="line"><span class="comment">// [30, 55, 40]</span></span><br></pre></td></tr></table></figure></p>
<p>同样的，作为函数式编程重要的一员，filter同样也不会对原有的数组产生影响，而是产生一个新的数组，从而达到链式调用的目的，因此我们就可以猜想它的实现逻辑大致就是下面这样的：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">Arra.prototype.filter = <span class="function"><span class="keyword">function</span>(<span class="params">fn</span>)</span>&#123;</span><br><span class="line">    <span class="keyword">let</span> result = []</span><br><span class="line">    <span class="keyword">let</span> temp</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i=<span class="number">0</span>; i&lt;<span class="keyword">this</span>.length; i++) &#123;</span><br><span class="line">        <span class="keyword">if</span>(i <span class="keyword">in</span> <span class="keyword">this</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (temp = fn.call(<span class="literal">undefined</span>, <span class="keyword">this</span>[i], i, <span class="keyword">this</span>)) &#123;</span><br><span class="line">                result.push(<span class="keyword">this</span>[i])</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> result</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<h3 id="2-7-Array-reduce"><a href="#2-7-Array-reduce" class="headerlink" title="2.7 Array.reduce"></a>2.7 Array.reduce</h3><p>Array.reduce(callback,[initialValue])方法对累计器和数组中的每个元素（从左到右）应用一个函数，将其简化为单个值。它接受四个参数:</p>
<ul>
<li>Accumulator (acc) (累计器)</li>
<li>Current Value (cur) (当前值)</li>
<li>Current Index (idx) (当前索引)</li>
<li>Source Array (src) (源数组)</li>
</ul>
<p>执行起来就是这样的：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">[<span class="number">0</span>, <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>].reduce(<span class="function"><span class="keyword">function</span>(<span class="params">accumulator, currentValue, currentIndex, array</span>)</span>&#123;</span><br><span class="line">  <span class="keyword">return</span> accumulator + currentValue;</span><br><span class="line">&#125;) <span class="comment">// 10</span></span><br></pre></td></tr></table></figure>
<p>reduce的功能非常强大，它同样也可以模拟map以及filter，比如模拟map:<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> array = [<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>]</span><br><span class="line"><span class="keyword">let</span> array1 = array.map(<span class="function">(<span class="params">v</span>) =&gt;</span> v+<span class="number">1</span>)</span><br><span class="line"><span class="comment">//用reduce实现为：</span></span><br><span class="line"><span class="keyword">let</span> array2 = array.reduce(<span class="function">(<span class="params">result, v</span>)=&gt;</span> &#123;</span><br><span class="line">     result.push(v + <span class="number">1</span>)</span><br><span class="line">     <span class="keyword">return</span> result</span><br><span class="line"> &#125;,[ ])</span><br></pre></td></tr></table></figure></p>
<p>同样我们也能来模拟filter:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> array1 = array.filter( <span class="function">(<span class="params">v</span>) =&gt;</span> v % <span class="number">2</span> === <span class="number">0</span> )</span><br><span class="line"><span class="comment">//用reduce实现为：</span></span><br><span class="line"><span class="keyword">let</span> array2 = array.reduce(<span class="function">(<span class="params">result, v</span>) =&gt;</span> &#123;</span><br><span class="line">     <span class="keyword">if</span>(v % <span class="number">2</span> === <span class="number">0</span>)&#123;</span><br><span class="line">        result.push(v)</span><br><span class="line">        &#125;</span><br><span class="line">     <span class="keyword">return</span> result</span><br><span class="line"> &#125;, [])</span><br></pre></td></tr></table></figure>
<p>是不是很有意思~ 当然平常开发的时候别这么做，因为使用指定的API来完成指定的任务会让代码变得更可读，更以维护~</p>
<h2 id="三、关于高阶函数的性能"><a href="#三、关于高阶函数的性能" class="headerlink" title="三、关于高阶函数的性能"></a>三、关于高阶函数的性能</h2><p>关于函数式编程最近在社区发生了很多的讨论。以我个人的看法，函数式编程在开发一些普通的应用或当应用变大时，其声明式的、纯函数式的，都为项目代码的可维护性以及可读性提供了很好的支持，这也是react最近出来的hooks以及redux还有Rx.js如此火爆的原因。但同时，我们也应该更具具体情况来考虑使用怎样的编程方式，比如我们在解决一些CV或较为底层的开发的时候，函数式编程可能就不能满足我们的开发需求，毕竟函数式编程在性能方面还是有所缺失的，比如在上面所说的那些高阶函数与原生for循环之间在性能上区别:</p>
<blockquote>
<p><a href="https://jsperf.com/for-foreach-reduce" target="_blank" rel="noopener">https://jsperf.com/for-foreach-reduce</a><br><img src="https://images.gitee.com/uploads/images/2018/1209/181116_7e7cdfaa_1575229.png" alt="输入图片说明" title="捕获.PNG"></p>
</blockquote>
<p>还有forEach、for、reduce之间的性能差距：</p>
<p><img src="https://images.gitee.com/uploads/images/2018/1209/182525_96336504_1575229.png" alt="输入图片说明" title="性能2.PNG"></p>
<p>可以看到，虽然浏览器引擎已经对这些高阶函数进行了优化，但比起原生的for循环这些高阶函数的执行效率还是有所差别的~因此在使用函数式编程的时候还是需要考虑项目实际的需要，当然我个人认为只要不是对性能要求非常严格的情况都可以使用函数式编程来增加我们写的代码的可读性和可维护性。</p>

          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://srtian96.gitee.io/blog/blog/2018/12/01/JavaScript字符串的处理/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="Srtian">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/blog/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Srtian'Blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/blog/2018/12/01/JavaScript字符串的处理/" itemprop="url">JavaScript数据处理之字符串</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-12-01T19:56:20+08:00">
                2018-12-01
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/JavaScript/" itemprop="url" rel="index">
                    <span itemprop="name">JavaScript</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          
            <div class="post-wordcount">
              
                
                <span class="post-meta-item-icon">
                  <i class="fa fa-file-word-o"></i>
                </span>
                
                  <span class="post-meta-item-text">字数统计&#58;</span>
                
                <span title="字数统计">
                  1,652
                </span>
              

              
                <span class="post-meta-divider">|</span>
              

              
                <span class="post-meta-item-icon">
                  <i class="fa fa-clock-o"></i>
                </span>
                
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                
                <span title="阅读时长">
                  7
                </span>
              
            </div>
          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <h3 id="一、字符串处理"><a href="#一、字符串处理" class="headerlink" title="一、字符串处理"></a>一、字符串处理</h3><p>字符串在编程中占据着举足轻重的作用，我们通常会使用字符串来储存一些文字之类的信息，以便计算机能够对这些信息进行检索或理解。因此首先我们先来看看JavaScript是如何来对字符串进行处理的。</p>
<h3 id="1-1-创建字符串"><a href="#1-1-创建字符串" class="headerlink" title="1.1 创建字符串"></a>1.1 创建字符串</h3><p>在JavaScript中有三种方法来创建字符串：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> string1 = <span class="string">'string 1'</span></span><br><span class="line"><span class="keyword">const</span> string2 = <span class="string">"string 2"</span></span><br><span class="line"><span class="keyword">const</span> string3 = <span class="string">`string 3`</span></span><br></pre></td></tr></table></figure>
<p>前两种在JavaScript语法定义上并没有太大的区别，都可以更具自己的个人习惯或者团队的编码风格来自行选择。但第三个，也就是<code></code>就比较特殊了，虽然它也能实现一个普通的字符串，但通常来说，我们一般使用它来创建一个模板字符串本：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 模板字符串</span></span><br><span class="line"><span class="keyword">const</span> example = <span class="string">'example'</span></span><br><span class="line"><span class="keyword">const</span> foo = <span class="string">`this is a <span class="subst">$&#123;example&#125;</span>`</span></span><br><span class="line"><span class="built_in">console</span>.log(foo) <span class="comment">// this is a exmaple</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 也可以创建多行文本，前后两条语句的执行结果完全一致</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">'string text line 1\n'</span> +</span><br><span class="line"><span class="string">'string text line 2'</span>)</span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">`string text line 1</span></span><br><span class="line"><span class="string">string text line 2`</span>)</span><br></pre></td></tr></table></figure>
<h3 id="1-2-字符串组合"><a href="#1-2-字符串组合" class="headerlink" title="1.2 字符串组合"></a>1.2 字符串组合</h3><p>上面就是我们在JavaScript中三种创建字符串的方式，但通常来说，我们常常需要将不同的信息通过各种方式进行拼装，从而产生一个我们所需要的字符串。比如我们上面所举的标签模板就是很常见的例子。</p>
<p>通常来说，最基本的字符串之间的连接我们可以直接通过+运算符来解决：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> example = <span class="string">'example'</span></span><br><span class="line"><span class="keyword">const</span> foo = <span class="string">'this is a '</span> + example</span><br><span class="line">conosle.log(foo) <span class="comment">// this is a exmaple</span></span><br></pre></td></tr></table></figure>
<p>但这种最基本的方式其实并不够优雅，且也只适用于数据量较小的字符串的拼接。若想数据量比较大时，需要将多个可变数据“嵌入”到一个模板中去，比如使用ES6的模板字符串来实现这个需求：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> example = <span class="string">'example'</span></span><br><span class="line"><span class="keyword">const</span> foo = <span class="string">`this is a <span class="subst">$&#123;example&#125;</span>`</span></span><br><span class="line"><span class="built_in">console</span>.log(foo) <span class="comment">// this is a exmaple</span></span><br></pre></td></tr></table></figure></p>
<p>或者是这种优雅的模板方式:<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> f00 = <span class="string">'foo'</span></span><br><span class="line"><span class="keyword">const</span> bar = <span class="string">'bar'</span></span><br><span class="line"><span class="keyword">let</span> url = <span class="string">`</span></span><br><span class="line"><span class="string">    www.srtian96.gitee.io/blog/</span></span><br><span class="line"><span class="string">    ?foo=<span class="subst">$&#123;foo&#125;</span></span></span><br><span class="line"><span class="string">    &amp;bar=<span class="subst">$&#123;bar&#125;</span></span></span><br><span class="line"><span class="string">`</span></span><br><span class="line"><span class="built_in">console</span>.log(url) <span class="comment">// www.srtian96.gitee.io/blog/?foo=foo&amp;bar=bar</span></span><br></pre></td></tr></table></figure></p>
<h3 id="1-3-处理字符串"><a href="#1-3-处理字符串" class="headerlink" title="1.3 处理字符串"></a>1.3 处理字符串</h3><p>在创建完字符串后，我们所要做的就是对这些字符串来进行处理了。下面我在MDN上随便截取的一段话：</p>
<blockquote>
<p>As a group, the standards are known as Web Components. In the year 2018 it’s easy to think of Web Components as old news. Indeed, early versions of the standards have been around in one form or another in Chrome since 2014, and polyfills have been clumsily filling the gaps in other browsers.<br>After some quality time in the standards committees, the Web Components standards were refined from their early form, now called version 0, to a more mature version 1 that is seeing implementation across all the major browsers. Firefox 63 added support for two of the tent pole standards, Custom Elements and Shadow DOM, so I figured it’s time to take a closer look at how you can play HTML inventor!</p>
</blockquote>
<p>我们先来分析一下这一段话，中间充斥着大小写的字母，标点符号，以及空格。此外还需要考虑的是HELLO、hello 和 Hello 的意思其实都是一样的，所以我们需要先完成以下预处理任务：</p>
<ul>
<li>去除标点符号及数字</li>
<li>将所有的大写字母转为小写</li>
</ul>
<h4 id="1-3-1-去除标点符号及数字"><a href="#1-3-1-去除标点符号及数字" class="headerlink" title="1.3.1 去除标点符号及数字"></a>1.3.1 去除标点符号及数字</h4><p>在纯英文的情况下，去除标点符号其实本质上就是只保留英文字母。因此在进行筛选时，我们只需要直接筛选出英文字母和空格就行了。这里我们可以使用 ASCII 码进行甄别。大写字母的 ASCII 码范围为 65 到 90，而小写字母则为 97 到 122，空格的 ASCII 码为 32，换行符的 ASCII 码为 10。在 JavaScript 可以用 string.charCodeAt() 方法获取字符的 ASCII 码：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> getText = <span class="function">(<span class="params">words</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">let</span> word = words</span><br><span class="line">    <span class="keyword">let</span> text = <span class="string">''</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; word.length; i++) &#123;</span><br><span class="line">        <span class="keyword">const</span> k = word[i]</span><br><span class="line">        <span class="keyword">const</span> asciiCode = k.charCodeAt()</span><br><span class="line">        <span class="keyword">if</span> ((asciiCode &gt;= <span class="number">65</span> &amp;&amp; asciiCode &lt;= <span class="number">90</span>) || (asciiCode &gt;= <span class="number">97</span> &amp;&amp; asciiCode &lt;= <span class="number">122</span>) || asciiCode === <span class="number">32</span>) &#123;</span><br><span class="line">            text += k</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> text</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>通过上面这个函数，我们就能够成功的将文本进行处理得到纯英语字母的文字了。</p>
<h4 id="1-3-2-大写转小写"><a href="#1-3-2-大写转小写" class="headerlink" title="1.3.2 大写转小写"></a>1.3.2 大写转小写</h4><p>前面我们在上面有提到，大小写的英语字母都有自己所对应的ASCII码，我们可以使用 string.charCodeAt() 方法来获取字符的 ASCII 码，同时JavaScript也提供给我们 String.fromCharCode()，来讲ASCII，码转化为对于的字符。而大写字母的ASCII码只要加上32就可以的达到对应的小写字母的ASCII码值了：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> toBeLower = <span class="function">(<span class="params">words</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">let</span> word = words</span><br><span class="line">    <span class="keyword">let</span> lower = <span class="string">''</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; words.length; ++i) &#123;</span><br><span class="line">        <span class="keyword">const</span> k = words[i]</span><br><span class="line">        <span class="keyword">const</span> asciiCode = k.charCodeAt()</span><br><span class="line">        <span class="keyword">if</span> (asciiCode &gt;= <span class="number">65</span> &amp;&amp; asciiCode &lt;= <span class="number">90</span>) &#123;</span><br><span class="line">            lower += <span class="built_in">String</span>.fromCharCode(asciiCode + <span class="number">32</span>)</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            lower += k</span><br><span class="line">            &#125; </span><br><span class="line">        &#125;</span><br><span class="line">    <span class="keyword">return</span> lower</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>但其实在JavaScript中我们没必要这么麻烦的去通过更改ASCII码来讲大写字母转为小写字母，我们可以直接调用JavaScript所提供的API：string.toLowerCase(),所以我们想将一些文字进行处理，就可以这么做:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> getPureText = <span class="function">(<span class="params">words</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">let</span> word = words</span><br><span class="line">    <span class="keyword">let</span> text = <span class="string">''</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; word.length; i++) &#123;</span><br><span class="line">        <span class="keyword">const</span> k = word[i]</span><br><span class="line">        <span class="keyword">const</span> asciiCode = k.charCodeAt()</span><br><span class="line">        <span class="keyword">if</span> ((asciiCode &gt;= <span class="number">65</span> &amp;&amp; asciiCode &lt;= <span class="number">90</span>) || (asciiCode &gt;= <span class="number">97</span> &amp;&amp; asciiCode &lt;= <span class="number">122</span>) || asciiCode === <span class="number">32</span>) &#123;</span><br><span class="line">            text += k</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> text.toLowerCase()</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>将我从MDN随意获取的那段文字传入这个函数中去，就可以得到这么一段已经被处理的文字：</p>
<blockquote>
<p>as a group the standards are known as web components in the year  its easy to think of web components as old news indeed early versions of the standards have been around in one form or another in chrome since  and polyfills have been clumsily filling the gaps in other browsers after some quality time in the standards committees the web components standards were refined from their early form now called version  to a more mature version  that is seeing implementation across all the major browsers firefox  added support for two of the tent pole standards custom elements and shadow dom so i figured its time to take a closer look at how you can play html inventor</p>
</blockquote>
<p>当我们获得这么一段已经被处理的字符时，想要对其进行检索就变得更容易了，比如我想知道这段话到底有多少个单词：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> words = <span class="string">'......'</span> <span class="comment">// 这是上面的那段话</span></span><br><span class="line"><span class="keyword">const</span> newWords = getPureText(words).split(<span class="string">' '</span>)</span><br><span class="line"><span class="built_in">console</span>.log(newWords.length) <span class="comment">// 123</span></span><br></pre></td></tr></table></figure>
<p>我们可以很简单的得到这个结果。当然如果你对正则表达式足够熟悉的话，直接可以使用正则表达式来实现这个目的，代码非常简单：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> getPureText = <span class="function">(<span class="params">words</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> word = words.toLowerCase().match(<span class="regexp">/\w+/g</span>)</span><br><span class="line">    <span class="keyword">return</span> word</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">console</span>.log(getPureText(words).length)</span><br></pre></td></tr></table></figure>

          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://srtian96.gitee.io/blog/blog/2018/11/11/使用Three.js+requestAnimationFrame实现一个3D动态效果/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="Srtian">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/blog/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Srtian'Blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/blog/2018/11/11/使用Three.js+requestAnimationFrame实现一个3D动态效果/" itemprop="url">使用Three.js+requestAnimationFrame实现一个3D动态效果</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-11-11T18:54:35+08:00">
                2018-11-11
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/Three-js/" itemprop="url" rel="index">
                    <span itemprop="name">Three.js</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          
            <div class="post-wordcount">
              
                
                <span class="post-meta-item-icon">
                  <i class="fa fa-file-word-o"></i>
                </span>
                
                  <span class="post-meta-item-text">字数统计&#58;</span>
                
                <span title="字数统计">
                  1,441
                </span>
              

              
                <span class="post-meta-divider">|</span>
              

              
                <span class="post-meta-item-icon">
                  <i class="fa fa-clock-o"></i>
                </span>
                
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                
                <span title="阅读时长">
                  6
                </span>
              
            </div>
          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <h3 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h3><p>最近几天事情很多，学校在教育评估，不能在宿舍待着，因此笔记也就没做了。但对于Three.js的学习还算不错，将其基本的概念算是了解的差不多了。但总是要有成果的，于是经过一番思索总算是搞出了一个3D效果的正方体，算是对这几天不怎么有效率的学习的一个总结。</p>
<h3 id="一、动态3D的生成"><a href="#一、动态3D的生成" class="headerlink" title="一、动态3D的生成"></a>一、动态3D的生成</h3><p>要实现一个3D动态效果，首先要做的就是先实现一个2D的效果（废话。。）。而使用Three.js来实现一个3D效动态果的正方体倒是不难，首先我们需要一个纯白的HTML：</p>
<figure class="highlight plain"><figcaption><span>html></span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line">&lt;html&gt;</span><br><span class="line">&lt;head&gt;</span><br><span class="line">    &lt;meta charset=&quot;utf-8&quot; /&gt;</span><br><span class="line">    &lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot;&gt;</span><br><span class="line">    &lt;title&gt;My 3D show&lt;/title&gt;</span><br><span class="line">    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1&quot;&gt;</span><br><span class="line">    &lt;script src=&quot;./three.min.js&quot;&gt;&lt;/script&gt;</span><br><span class="line">&lt;/head&gt;</span><br><span class="line">&lt;body&gt;</span><br><span class="line">    &lt;script&gt;&lt;/script&gt;</span><br><span class="line">&lt;/body&gt;</span><br><span class="line">&lt;/html&gt;</span><br><span class="line">&lt;!DOCTYPE html&gt;</span><br><span class="line">&lt;html&gt;</span><br><span class="line">&lt;head&gt;</span><br><span class="line">    &lt;meta charset=&quot;utf-8&quot; /&gt;</span><br><span class="line">    &lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot;&gt;</span><br><span class="line">    &lt;title&gt;Page Title&lt;/title&gt;</span><br><span class="line">    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1&quot;&gt;</span><br><span class="line">    &lt;script src=&quot;./three.min.js&quot;&gt;&lt;/script&gt;</span><br><span class="line">&lt;/head&gt;</span><br><span class="line">&lt;body&gt;</span><br><span class="line">    &lt;script&gt;</span><br><span class="line">        </span><br><span class="line">    &lt;/script&gt;</span><br><span class="line">&lt;/body&gt;</span><br><span class="line">&lt;/html&gt;</span><br></pre></td></tr></table></figure>
<p>然后我们需要做的就是将我们的JavaScript代码写在script标签内就行了。在Three.js中，有三个非常重要的东西：</p>
<ul>
<li>渲染器（Renderer）</li>
<li>场景（Scene）</li>
<li>照相机（Camera）</li>
</ul>
<p>用比较通俗的话来讲，场景就是我们摄影的时候的背景或场景，而照相机则是可以定义我们所拍摄的物体的形状之内的东西，而渲染器则负责将场景与照相机所拍出来的东西给渲染出来。正是这三者的合力，才能让我们产生一个3D效果。首先我们需要生成一个canvas：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> w = <span class="built_in">window</span>.innerWidth</span><br><span class="line"><span class="keyword">const</span> h = <span class="built_in">window</span>.innerHeight</span><br><span class="line"><span class="keyword">const</span> renderer = <span class="keyword">new</span> THREE.WebGLRenderer()</span><br><span class="line">renderer.setSize(w, h)</span><br><span class="line">renderer.setClearColor(<span class="number">0xb9d3ff</span>, <span class="number">1.1</span>)</span><br><span class="line"><span class="built_in">document</span>.body.appendChild(renderer.domElement)</span><br></pre></td></tr></table></figure>
<p>现在我们就在我们页面插入一个canvas了，这段代码倒是很好理解，先是获取window的宽与高，然后是由渲染器设定大小与背景颜色。需要注意的是，现在添加背景颜色并没有用，因为还有相关的光源没有设置。接下来我们要做的就是解决场景方面的问题：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> scene = <span class="keyword">new</span> THREE.Scene();</span><br><span class="line"><span class="comment">// 创建网格模型</span></span><br><span class="line"><span class="keyword">const</span> geometry = <span class="keyword">new</span> THREE.BoxGeometry(<span class="number">80</span>, <span class="number">80</span>, <span class="number">80</span>)</span><br><span class="line"><span class="keyword">const</span> material = <span class="keyword">new</span> THREE.MeshLambertMaterial(&#123;</span><br><span class="line">    color: <span class="number">0x0000ff</span></span><br><span class="line">&#125;)</span><br><span class="line"><span class="keyword">const</span> mesh = <span class="keyword">new</span> THREE.Mesh(geometry, material)</span><br><span class="line">scene.add(mesh)</span><br><span class="line"><span class="comment">// 光源设置</span></span><br><span class="line"><span class="keyword">const</span> point = <span class="keyword">new</span> THREE.PointLight(<span class="number">0xffffff</span>)</span><br><span class="line">point.position.set(<span class="number">400</span>, <span class="number">200</span>, <span class="number">300</span>)</span><br><span class="line">scene.add(point)</span><br><span class="line"><span class="keyword">const</span> ambient = <span class="keyword">new</span> THREE.AmbientLight(<span class="number">0x444444</span>)</span><br><span class="line">scene.add(ambient)</span><br></pre></td></tr></table></figure>
<p>如上的代码就是关于场景的设置，我们可以通过Three.js已经封装好的相关方法来创建我们需要的正方体，同时也将我们所需要的光源等元素加入到scene中，使用add加入的元素都可以在scene的children中找到：</p>
<p><img src="https://images.gitee.com/uploads/images/2018/1111/181355_e0c0e7e1_1575229.png" alt="输入图片说明" title="scenechildren.PNG"></p>
<p>搞定场景后，我们就需要搞定照相机了。在图形学中的照相机与我们日常生活中所定义的照相机是不一样的，在图形学中的照相机，它定义了我们所构造的三维空间到投影在显示屏上的二维空间的投影的方式。而照相机只是一种类比而已。</p>
<p>而根据投影方式的不同，照相机又有以下两种分类：</p>
<ol>
<li>正交投影照相机（Orthographic Camera）</li>
<li>透视投影照相机</li>
</ol>
<p>我们这次所使用的就是正交投影照相机：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> k = w / h <span class="comment">// 窗口宽高比</span></span><br><span class="line"><span class="keyword">const</span> s = <span class="number">200</span> <span class="comment">// 三维场景显示范围</span></span><br><span class="line"><span class="keyword">const</span> camera = <span class="keyword">new</span> THREE.OrthographicCamera(-s * k, s * k, s, -s, <span class="number">1</span>, <span class="number">1000</span>)</span><br><span class="line">camera.position.set(<span class="number">200</span>, <span class="number">300</span>, <span class="number">200</span>)</span><br><span class="line">camera.lookAt(scene.position)</span><br></pre></td></tr></table></figure>
<p>到这时，我们已经完成了渲染器，场景，照相机的相关设置了，接下来就只需将使用渲染器将照相机与场景渲染出来即可：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">renderer.render(scene, camera)</span><br></pre></td></tr></table></figure>
<p>我们就能得到一个静态的3D正方体了：</p>
<p><img src="https://images.gitee.com/uploads/images/2018/1111/182819_9c41a7dc_1575229.png" alt="image" title="3D1.PNG"></p>
<h3 id="二、动态设置"><a href="#二、动态设置" class="headerlink" title="二、动态设置"></a>二、动态设置</h3><p>想让这个静态的3D图像动态化，其实思路就是多次执行render方法，从而覆盖之前的render放来，从而达到动态的效果。而多次执行render总的来说有以下两个方案：</p>
<ul>
<li>setInterval()：按照所设定的周期去执行函数。</li>
<li>requestAnimationFrame()：请求浏览器执行下一帧动画。</li>
</ul>
<p>使用setInterval()我们可以这样做：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> myRender = <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    renderer.render(scene, camera)</span><br><span class="line">    mesh.rotateY(<span class="number">0.01</span>)</span><br><span class="line">&#125;</span><br><span class="line">setInterval(<span class="string">"myRender()"</span>, <span class="number">25</span>)</span><br></pre></td></tr></table></figure></p>
<p>但在我的理解来看,setInterval()还是比较适合与时钟等时间间隔要求很严格的应用，而requestAnimationFrame()则适合动画等应用，因为它请求的是下一帧的动画，所以其动画也会较为流畅：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> myRender = <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    renderer.render(scene, camera)</span><br><span class="line">    mesh.rotateY(<span class="number">0.01</span>)</span><br><span class="line">    requestAnimationFrame(myRender)</span><br><span class="line">&#125;</span><br><span class="line">myRender()</span><br></pre></td></tr></table></figure></p>
<p>这样我们就能实现一个旋转的3D正方体了。全部的JavaScript代码如下：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> w = <span class="built_in">window</span>.innerWidth</span><br><span class="line"><span class="keyword">const</span> h = <span class="built_in">window</span>.innerHeight</span><br><span class="line"><span class="comment">// 场景</span></span><br><span class="line"><span class="keyword">const</span> scene = <span class="keyword">new</span> THREE.Scene();</span><br><span class="line"><span class="comment">// 创建网格模型</span></span><br><span class="line"><span class="keyword">const</span> geometry = <span class="keyword">new</span> THREE.BoxGeometry(<span class="number">80</span>, <span class="number">80</span>, <span class="number">80</span>)</span><br><span class="line"><span class="keyword">const</span> material = <span class="keyword">new</span> THREE.MeshLambertMaterial(&#123;</span><br><span class="line">    color: <span class="number">0x0000ff</span></span><br><span class="line">&#125;)</span><br><span class="line"><span class="keyword">const</span> mesh = <span class="keyword">new</span> THREE.Mesh(geometry, material)</span><br><span class="line">scene.add(mesh)</span><br><span class="line"><span class="comment">// 光源设置</span></span><br><span class="line"><span class="keyword">const</span> point = <span class="keyword">new</span> THREE.PointLight(<span class="number">0xffffff</span>)</span><br><span class="line">point.position.set(<span class="number">400</span>, <span class="number">200</span>, <span class="number">300</span>)</span><br><span class="line">scene.add(point)</span><br><span class="line"><span class="keyword">const</span> ambient = <span class="keyword">new</span> THREE.AmbientLight(<span class="number">0x444444</span>)</span><br><span class="line">scene.add(ambient)</span><br><span class="line"><span class="built_in">console</span>.log(scene.children)</span><br><span class="line"><span class="comment">// 相机</span></span><br><span class="line"><span class="keyword">const</span> k = w / h <span class="comment">// 窗口宽高比</span></span><br><span class="line"><span class="keyword">const</span> s = <span class="number">200</span> <span class="comment">// 三维场景显示范围</span></span><br><span class="line"><span class="keyword">const</span> camera = <span class="keyword">new</span> THREE.OrthographicCamera(-s * k, s * k, s, -s, <span class="number">1</span>, <span class="number">1000</span>)</span><br><span class="line">camera.position.set(<span class="number">200</span>, <span class="number">300</span>, <span class="number">200</span>)</span><br><span class="line">camera.lookAt(scene.position)</span><br><span class="line"><span class="comment">// 渲染器</span></span><br><span class="line"><span class="keyword">const</span> renderer = <span class="keyword">new</span> THREE.WebGLRenderer()</span><br><span class="line">renderer.setSize(w, h)</span><br><span class="line">renderer.setClearColor(<span class="number">0xb9d3ff</span>, <span class="number">1.1</span>)</span><br><span class="line"><span class="built_in">document</span>.body.appendChild(renderer.domElement)</span><br><span class="line"><span class="keyword">const</span> myRender = <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">renderer.render(scene, camera)</span><br><span class="line">mesh.rotateY(<span class="number">0.01</span>)</span><br><span class="line">requestAnimationFrame(myRender)</span><br><span class="line">&#125;</span><br><span class="line">myRender()</span><br></pre></td></tr></table></figure>

          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://srtian96.gitee.io/blog/blog/2018/11/05/three.js学习笔记（一）/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="Srtian">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/blog/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Srtian'Blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/blog/2018/11/05/three.js学习笔记（一）/" itemprop="url">Three.js学习笔记（一）</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-11-05T13:42:55+08:00">
                2018-11-05
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/Three-js/" itemprop="url" rel="index">
                    <span itemprop="name">Three.js</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          
            <div class="post-wordcount">
              
                
                <span class="post-meta-item-icon">
                  <i class="fa fa-file-word-o"></i>
                </span>
                
                  <span class="post-meta-item-text">字数统计&#58;</span>
                
                <span title="字数统计">
                  990
                </span>
              

              
                <span class="post-meta-divider">|</span>
              

              
                <span class="post-meta-item-icon">
                  <i class="fa fa-clock-o"></i>
                </span>
                
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                
                <span title="阅读时长">
                  4
                </span>
              
            </div>
          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <h3 id="一、什么是three-js？"><a href="#一、什么是three-js？" class="headerlink" title="一、什么是three.js？"></a>一、什么是three.js？</h3><p>学习一门东西，我一直以来的思路就是先要搞清楚，我们为什么需要这项技术，以及它能给我们带来什么，对于three.js我也是如此。</p>
<p>首先Three.js封装了底层的图形接口，让我们能用简单的代码实现三维场景的渲染,本质上可以看做WebGL的上层的封装。</p>
<p>那么什么又是webGL呢？WebGL是基于OpenGL ES 2.0的Web标准，可以通过HTML5 Canvas元素作为DOM接口访问。<br>简单来说，WebGL其实就是将OpenGL （ES）移植到了网页平台，让我们使用JavaScript就可以在网页上实现三维图形的渲染了。</p>
<p>而之所以我们需要对WebGL进行封装，而不是直接去使用WebGL，就是由于WebGL的代码书写起来太过复杂，往往实现一个小的效果，却需要几百行代码，这极大的影响了我们的日常开发效率，因此才会有Tree.js的出现，目的就是提高开发效率，减少一定的学习成本。</p>
<blockquote>
<p>差异方面具体可看：《Three.js入门指南》一书中的比较：<a href="http://www.ituring.com.cn/book/miniarticle/49782" target="_blank" rel="noopener">http://www.ituring.com.cn/book/miniarticle/49782</a></p>
</blockquote>
<blockquote>
<p>还有youtube上的这个视频也不错：<a href="https://www.youtube.com/watch?v=YKzyhcyAijo" target="_blank" rel="noopener">https://www.youtube.com/watch?v=YKzyhcyAijo</a><br>（虽然感觉这老哥说的英语不咋清楚，但总体看上两遍后也就可以听清楚了）</p>
</blockquote>
<h3 id="二、Three-js初探"><a href="#二、Three-js初探" class="headerlink" title="二、Three.js初探"></a>二、Three.js初探</h3><p>three.js是画在canvans上的，因此我们首先需要申明canvans标签：</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;!DOCTYPE html&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"utf-8"</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">http-equiv</span>=<span class="string">"X-UA-Compatible"</span> <span class="attr">content</span>=<span class="string">"IE=edge"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">title</span>&gt;</span>First Three.js Demo<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">name</span>=<span class="string">"viewport"</span> <span class="attr">content</span>=<span class="string">"width=device-width, initial-scale=1"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">style</span>&gt;</span><span class="undefined"></span><span class="tag">&lt;/<span class="name">style</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">"three.min.js"</span>&gt;</span><span class="undefined"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">canvas</span> <span class="attr">id</span>=<span class="string">"mainCanvas"</span> <span class="attr">width</span>=<span class="string">"400px"</span> <span class="attr">height</span>=<span class="string">"300px"</span> &gt;</span><span class="tag">&lt;/<span class="name">canvas</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="undefined"></span></span><br><span class="line"><span class="javascript">    <span class="keyword">const</span> init = <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span></span><br><span class="line"><span class="undefined">&#125;</span></span><br><span class="line"><span class="undefined"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre></td></tr></table></figure>
<p>上面的幕布已经准备少了，下面就是需要使用Three.js来画东西了。首先要明确的是Three.js是由下面三者构成的：</p>
<ul>
<li>渲染器（Renderer）</li>
<li>场景（Scene）</li>
<li>照相机（Camera）</li>
</ul>
<h4 id="2-1-渲染器（Renderer）"><a href="#2-1-渲染器（Renderer）" class="headerlink" title="2.1 渲染器（Renderer）"></a>2.1 渲染器（Renderer）</h4><p>渲染器将和Canvas元素进行绑定,在这里我们使用的就是WebGLRenderer：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> renderer = <span class="keyword">new</span> THREE.WebGLRenderer(&#123;</span><br><span class="line">    canvas: <span class="built_in">document</span>.getElementById(<span class="string">'mainCanvas'</span>)</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure></p>
<p>当然，我们也能使用Three.js来生成一个canvas，这样我们就无需再将渲染器与canvas进行绑定了：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> renderer = <span class="keyword">new</span> THREE.WebGLRenderer()</span><br><span class="line">renderer.setSize(<span class="number">400</span>, <span class="number">300</span>) <span class="comment">// 表示设置Canvas的宽400像素，高300像素</span></span><br><span class="line"><span class="built_in">document</span>.getElementsByTagName(<span class="string">'body'</span>)[<span class="number">0</span>].appendChild(renderer.domElement) <span class="comment">// 将渲染器对应的Canvas元素添加到&lt;body&gt;中</span></span><br></pre></td></tr></table></figure>
<p>然后我就能来画这个canvas了：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">renderer.setClearColor(<span class="number">0x000000</span>)</span><br></pre></td></tr></table></figure>
<p>就能看到一块黑色的背景：<br><img src="https://images.gitee.com/uploads/images/2018/1105/124811_cf5ca4a5_1575229.png" alt="输入图片说明" title="three1.PNG"></p>
<h4 id="2-2-场景（Scene）"><a href="#2-2-场景（Scene）" class="headerlink" title="2.2 场景（Scene）"></a>2.2 场景（Scene）</h4><p>我么在Three.js中所添加的物体都是添加到场景中的，因此场景其实就相当于一个大容器。一般说，场景里没有很复杂的操作，在程序最开始的时候进行实例化，然后将物体添加到场景中即可：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> scene = <span class="keyword">new</span> THREE.Scene()</span><br></pre></td></tr></table></figure>
<h4 id="2-3-照相机（Camera）"><a href="#2-3-照相机（Camera）" class="headerlink" title="2.3 照相机（Camera）"></a>2.3 照相机（Camera）</h4><p>在Three.js中，照相机可以说是最重要的一个工具了，我们使用所添加的物件都是需要它来进行定义的。这里，我们定义了一个透视投影的照相机：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> camera = <span class="keyword">new</span> THREE.PerspectiveCamera(<span class="number">45</span>, <span class="number">4</span> / <span class="number">3</span>, <span class="number">1</span>, <span class="number">1000</span>)</span><br><span class="line">camera.position.set(<span class="number">0</span>, <span class="number">0</span>, <span class="number">5</span>)</span><br><span class="line">scene.add(camera)</span><br></pre></td></tr></table></figure>
<h4 id="2-4-画东西"><a href="#2-4-画东西" class="headerlink" title="2.4 画东西"></a>2.4 画东西</h4><p>在WebGL和Three.js中都有坐标系这一概念，他们两个使用的坐标系是右手坐标系：<br><img src="http://www.ituring.com.cn/download/01YmPz57VN7b" alt="image"><br>我们在画东西的时候，就是根据这个坐标系来实现我们的物体的，比如我们需要实现一个长方形：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> cube = <span class="keyword">new</span> THREE.Mesh(<span class="keyword">new</span> THREE.CubeGeometry(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>),</span><br><span class="line">    <span class="keyword">new</span> THREE.MeshBasicMaterial(&#123;</span><br><span class="line">        color: <span class="number">0xff0000</span></span><br><span class="line">    &#125;)</span><br><span class="line">)</span><br><span class="line">scene.add(cube) <span class="comment">// 将长方体加入场景</span></span><br></pre></td></tr></table></figure>
<p>最后使用渲染器将场景和照相机渲染出来：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">renderer.render(scene, camera)</span><br></pre></td></tr></table></figure>
<p>总的效果如下：</p>
<p><img src="https://images.gitee.com/uploads/images/2018/1105/130112_0d0167a2_1575229.png" alt="输入图片说明" title="three2.PNG"></p>
<p>参考资料：</p>
<ul>
<li>《Three.js入门指南》</li>
<li><a href="https://www.youtube.com/watch?v=YKzyhcyAijo" target="_blank" rel="noopener">https://www.youtube.com/watch?v=YKzyhcyAijo</a></li>
</ul>

          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
      

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://srtian96.gitee.io/blog/blog/2018/11/02/Rx.js学习笔记（一）——创建数据流/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="Srtian">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/blog/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Srtian'Blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
                
                <a class="post-title-link" href="/blog/2018/11/02/Rx.js学习笔记（一）——创建数据流/" itemprop="url">Rx.js学习笔记（一）——创建数据流</a></h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-11-02T00:11:47+08:00">
                2018-11-02
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/Rx-js/" itemprop="url" rel="index">
                    <span itemprop="name">Rx.js</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          
            <div class="post-wordcount">
              
                
                <span class="post-meta-item-icon">
                  <i class="fa fa-file-word-o"></i>
                </span>
                
                  <span class="post-meta-item-text">字数统计&#58;</span>
                
                <span title="字数统计">
                  2,522
                </span>
              

              
                <span class="post-meta-divider">|</span>
              

              
                <span class="post-meta-item-icon">
                  <i class="fa fa-clock-o"></i>
                </span>
                
                  <span class="post-meta-item-text">阅读时长 &asymp;</span>
                
                <span title="阅读时长">
                  10
                </span>
              
            </div>
          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        
          
            <h3 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h3><p>这些笔记大部分都是阅读《深入浅出RxJS》一书的笔记。这是我第二次看了，第一次知识粗略的翻过，回想起来没有什么印象，所以决定再看一次。而这一次一边看一边做笔记，这样应该会印象更为深刻一点吧。</p>
<h3 id="一、Rx-js-操作符——创建数据流"><a href="#一、Rx-js-操作符——创建数据流" class="headerlink" title="一、Rx.js 操作符——创建数据流"></a>一、Rx.js 操作符——创建数据流</h3><p>在Rx.js中，有很多操作符，从哪开始学习是一个很让人纠结的问题。而数据流是一个非常重要的概念。并且在实际的开发中，数据流就是以Observable类的实例对象而存在的，因此学习Rx.js，从创建数据流开始是一个很好的开端。</p>
<p>总的来说，创建数据流的操作符有以下这些：</p>
<ul>
<li>create 直接创建数据流</li>
<li>of 根据有限的数据产生同步的数据流</li>
<li>range 产生一个数值范围内的数据</li>
<li>generate 以循环方式产生数据</li>
<li>repeat和repeatWhen 重复产生数据流中的数据</li>
<li>empty 产生空数据流</li>
<li>throw 产生直接出错的数据流</li>
<li>never 产生永不完结的数据流</li>
<li>interval和timer 间隔给定时间持续产生数据</li>
<li>from 从数组等枚举类型数据产生数据流</li>
<li>fromPromise 从Promise对象产生数据流</li>
<li>fromEvent和fromEventPattern 从外部事件对象产生数据流</li>
<li>ajax 从Ajax请求结果产生数据流</li>
<li>defer 延迟产生数据流</li>
</ul>
<h3 id="二、创建同步数据流"><a href="#二、创建同步数据流" class="headerlink" title="二、创建同步数据流"></a>二、创建同步数据流</h3><p>同步的数据流，顾名思义，就是产生的数据流其数据之间不存在时间间隔，因此我们无需考虑时间的问题。</p>
<h4 id="2-1-create"><a href="#2-1-create" class="headerlink" title="2.1 create"></a>2.1 create</h4><p>create算是最简单的一个操作符了，它的功能非常简单，就是直接调用Observable的构造函数，其主要的逻辑如下：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Observable.create = <span class="function"><span class="keyword">function</span>(<span class="params">subscribe</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> Observable(subscribe)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>上面的代码就在Observable类的定义之中，因此我们使用create无需导入其他模块就可以直接使用了。</p>
<p>也正是由于这个原因，它与直接使用Observable构造函数没啥区别，因此我在大多数情况用不上它的。</p>
<h4 id="2-2-public-static-of-values-…T-scheduler-Scheduler-Observable"><a href="#2-2-public-static-of-values-…T-scheduler-Scheduler-Observable" class="headerlink" title="2.2 public static of(values: …T, scheduler: Scheduler): Observable"></a>2.2 public static of(values: …T, scheduler: Scheduler): Observable<t></t></h4><p>我们可以利用of操作符可以创建指定个数据集合的Observable对象。比如我们要产生包含三个正整数的Observable对象，那么我们就可以这样：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; Observable &#125; <span class="keyword">from</span> <span class="string">'rxjs/Observable'</span></span><br><span class="line"><span class="keyword">import</span> <span class="string">'rx.js/add/observable/of'</span></span><br><span class="line"><span class="keyword">const</span> source$ = Observable.of(<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>)</span><br><span class="line">source$.subscribe(</span><br><span class="line">    <span class="built_in">console</span>.log,</span><br><span class="line">    <span class="literal">null</span>,</span><br><span class="line">    ()=&gt;<span class="built_in">console</span>.log(<span class="string">'complete'</span>)</span><br><span class="line">)</span><br></pre></td></tr></table></figure>
<p>按照上面的代码，source$可以产生Observable对象，再被subscribe后就可以把参数1,2,3吐出来了，然后调用Observer的complete函数。需要注意的是，它吐出的数据是同步的，没有任何时间间隔。</p>
<p>此外，if产生的是Cold Observable，因此对于每一个Obervable都会重复突出同样的一组数据，所以可以反复使用。</p>
<p>of适用的场景就是已知不多的几个数据，想要把这些数据用Observable对象来封装，而且无需时间间隔，我们就能用和这个来处理。</p>
<h4 id="2-3-range"><a href="#2-3-range" class="headerlink" title="2.3 range"></a>2.3 range</h4><p>range操作符顾名思义，就是能产生一个连续的数字序列。我们只需指定一个范围的开始值与长度：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> numbers = Rx.Observable.range(<span class="number">1</span>, <span class="number">100</span>)</span><br><span class="line">numbers.subscribe(<span class="function"><span class="params">x</span> =&gt;</span> <span class="built_in">console</span>.log(x))</span><br></pre></td></tr></table></figure>
<p>同样的，range产生的也是同步的数据，它会一次性的将数据给推出来。此外我们也可以让他是小数：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> numbers = Rx.Observable.range(<span class="number">1.5</span>, <span class="number">100</span>)</span><br></pre></td></tr></table></figure>
<h4 id="2-4-generate"><a href="#2-4-generate" class="headerlink" title="2.4 generate"></a>2.4 generate</h4><p>generate类似一个for循环，我们可以设定一个初始值，每次递增这个值，知道满足每个条件的时候才会终止这个循环，循环体内可以根据值产生数据，比如我们想完成上面range的那个功能，我们就可以这样：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">const</span> range = <span class="function">(<span class="params">start, count</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> max = start + count</span><br><span class="line">    <span class="keyword">return</span> Observable.generate(</span><br><span class="line">    start, <span class="comment">// 初始值</span></span><br><span class="line">    value =&gt; value &lt; max, <span class="comment">// 条件语句</span></span><br><span class="line">    value =&gt; value + <span class="number">1</span>, <span class="comment">// 每次值的改变</span></span><br><span class="line">    value =&gt; value, <span class="comment">// 产生的结果</span></span><br><span class="line">    )</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>使用generate中，四个参数分别对应了for循环中的不同表达式，除了第一个参数是一个值以外，其他三个参数都是函数，我们应该保持三个参数都是纯函数，这才不会与函数式编程的原则相违背。</p>
<p>此外，在generate产生不限于数值序列的数据流，字符串也是可以的:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> source = Rx.Observable.gengerate(</span><br><span class="line">    <span class="string">'x'</span>,</span><br><span class="line">    value =&gt; value.length &lt;= <span class="number">3</span>,</span><br><span class="line">    value =&gt; value + <span class="string">'x'</span>,</span><br><span class="line">    value =&gt; value,</span><br><span class="line">)</span><br></pre></td></tr></table></figure>
<p>总的来说，在Rx.js中，我们在传统编程中用到for循环产生数据的情况，我们都可以使用generate来产生我们需要的数据流。</p>
<h4 id="2-5-repeat"><a href="#2-5-repeat" class="headerlink" title="2.5 repeat"></a>2.5 repeat</h4><p>repeat是一个实例操作符，它可以重复上游Observable中的数据若干次。比如我们想要重复的1,2,3的正整数序列10次，那么我们就能这样：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> source$ = Observable.of(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>)</span><br><span class="line"><span class="keyword">const</span> repeated$ = source$.repeat(<span class="number">10</span>)</span><br></pre></td></tr></table></figure>
<p>此外，repeat的重复功能依赖于上游的完结时机，所以，使用repeat很重要的就是要保证上游Observable对象最终一定会完结，那么repeat也就没有机会unsubscribe。其次，repeat的参数代表重复的次数，因此如果不传入这个参数，或者传入的参数是附属，就代表无限次的重复。</p>
<h4 id="2-6-empty"><a href="#2-6-empty" class="headerlink" title="2.6 empty"></a>2.6 empty</h4><p>empty代表一个直接完结的Observable对象，没有参数，不会残生任何数据，直接完结。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> source = Rx.Observable.empty()</span><br></pre></td></tr></table></figure>
<h4 id="2-7-throw"><a href="#2-7-throw" class="headerlink" title="2.7 throw"></a>2.7 throw</h4><p>throw所产生的Observable对象也是啥都不做，直接出错，而抛出的错误就是throw的参数：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> source = Rx.Observable.throw(<span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">'oops'</span>))</span><br></pre></td></tr></table></figure>
<h4 id="2-8-never"><a href="#2-8-never" class="headerlink" title="2.8 never"></a>2.8 never</h4><p>never操作符所产生的Observable对象真的啥都不做，即不突出数据，也不完结，也不会产生错误。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> source = Rx.Observable.never()</span><br></pre></td></tr></table></figure>
<h3 id="三、创建异步数据流"><a href="#三、创建异步数据流" class="headerlink" title="三、创建异步数据流"></a>三、创建异步数据流</h3><p>异步数据流，或者说异步Observable对象，是Rx.js的解决问题的一大利器，上面的那些操作符所产生的都是同步的数据流，而处理异步操作就需要使用其他的操作符了。</p>
<h4 id="3-1-interval和timer"><a href="#3-1-interval和timer" class="headerlink" title="3.1 interval和timer"></a>3.1 interval和timer</h4><p>interval和timer这两个操作符的地位等同于JavaScript中的setInterval和setTimeout，但功能并不完全一样。</p>
<p>interval接受一个数值类型的参数，代表产生数据的间隔，返回的Observable对象就按照这个时间间隔从零开始输出递增的整数序列：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> number$ = Rx.Observable.interval(<span class="number">1000</span>)</span><br></pre></td></tr></table></figure>
<p>比如上面这条语句，它会从在1秒钟的时候突出数据0.然后再2秒钟的时候突出数据1…..需要注意的是，这个数据流是不会完结的，因为interval操作符不会主动调用下游的complete,要想停止这个数据序列，就必须要做退订的操作。</p>
<p>还有一个问题，interval本身所产生的异步数据序列只能从0开始递增，但实际中我们可以通过与其他操作符相结合来实现我们对其数据产生的定制化的需求：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> source$ = Observable.interval(<span class="number">1000</span>)</span><br><span class="line"><span class="keyword">const</span> result$ = source$.map(x = x + <span class="number">1</span>)</span><br></pre></td></tr></table></figure>
<p>而timer则对于着setTimeout。time的第一个参数可以是一个数值，也可以是一个Date类型的对象。如果第一个参数是数值，代表的就是毫秒数，产生的Observable对象在指定毫秒之后会突出一个数据0，然后立即完结。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> source$ = Rx.Observable.timer(<span class="number">1000</span>)</span><br></pre></td></tr></table></figure>
<p>上面这个功能同样也可以通过传递一个Date对象给timer来实现：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> now = <span class="keyword">new</span> <span class="built_in">Date</span>()</span><br><span class="line"><span class="keyword">const</span> later = <span class="keyword">new</span> <span class="built_in">Date</span>(now.getTime() + <span class="number">1000</span>)</span><br><span class="line"><span class="keyword">const</span> source$ = Observable.timer(later)</span><br></pre></td></tr></table></figure>
<p>但我们使用Date对象作为参数，要看情况而定。如果我们明确了需要延时产生数据的时间间隔，那就应该用数值作为参数，如果明确的是一个时间点，那么就传入Date对象。</p>
<p>timer还支持第二个参数，它决定了各数据之间的时间间隔。如果使用它，那么就会产生一个持续突出数据的Observable对象，类似interval的数据流：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> source1$ = Rx.Obsevable.timer(<span class="number">2000</span>, <span class="number">1000</span>)</span><br><span class="line"><span class="keyword">const</span> source2$ = Rx.Obsevable.interval(<span class="number">2000</span>) <span class="comment">// source1$ 和 source2$ 所产生的数据流是一样的。</span></span><br></pre></td></tr></table></figure>
<h4 id="3-2-from"><a href="#3-2-from" class="headerlink" title="3.2 from"></a>3.2 from</h4><p>from可以说是创建类操作符中包容性最亲那个的一个了，因为它接受的参数只要像Observable就行了，然后就能根据参数中的数据阐述一个真正的Observable对象。比如以下这些：</p>
<ul>
<li>数组</li>
<li>字符串</li>
<li>Promise</li>
<li>generator</li>
<li>等等</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> source$ = Observable.from([<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>])</span><br></pre></td></tr></table></figure>
<h4 id="3-3-fromPromise"><a href="#3-3-fromPromise" class="headerlink" title="3.3 fromPromise"></a>3.3 fromPromise</h4><p>前面说的from操作符的参数可以使Promsie对象时，当这个Promsie成功结束时，from产生的Observable对象就会突出Promsie成功的结果，并且立即结束：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> promise = <span class="built_in">Promise</span>.resolve(<span class="string">'good'</span>)</span><br><span class="line"><span class="keyword">const</span> source$ = Observable.from(promise)</span><br><span class="line">source$.subscribe(</span><br><span class="line">    <span class="built_in">console</span>.log,</span><br><span class="line">    error = <span class="built_in">console</span>.log(<span class="string">'catch'</span>, error),</span><br><span class="line">    () =&gt; <span class="built_in">console</span>.log(<span class="string">'complete'</span>)</span><br><span class="line">)</span><br></pre></td></tr></table></figure>
<p>这种方式只有一个结果，所以当Promsie成功完成的时候，from也不知道不会再由新的数据了，所以立即完结了产生的Observable对象。</p>
<h4 id="3-4-fromEvent"><a href="#3-4-fromEvent" class="headerlink" title="3.4 fromEvent"></a>3.4 fromEvent</h4><p>fromEvent操作符在网页开发中最可能用到的操作符，以你为网页中总是要获取用户在网页中的操作时间，而fromEvent最常见的用法就是把DOM中的时间转化为Observable对象中的数据。</p>
<p>fromEvent接受两个参数，第一个参数是一个事件源，在浏览器中，最常见的事件源就是特定的DOM元素，第二个参数就是事件的名称，对于DOM事件就是click，mousemove这样的字符串。比如我们欧一段这样的HTML代码：</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">button</span> <span class="attr">id</span>=<span class="string">'clickMe'</span>&gt;</span>Click Me<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">'text'</span>&gt;</span>0<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure>
<p>当我们需要完成一个点击click时，下面的数字加一的需求时，我们就可以这样做：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> clickCount = <span class="number">0</span></span><br><span class="line"><span class="keyword">const</span> clickMe = doucment.querySelector(<span class="string">'#clickMe'</span>)</span><br><span class="line"><span class="keyword">const</span> text = doucment.querySelector(<span class="string">'#text'</span>)</span><br><span class="line"><span class="keyword">const</span> event$ = Rx.Observable.fromEvent(clickMe, <span class="string">'click'</span>)</span><br><span class="line">event$.subscribe(</span><br><span class="line">    () =&gt; &#123;</span><br><span class="line">        text.innerText = ++clickCount</span><br><span class="line">    &#125;</span><br><span class="line">)</span><br></pre></td></tr></table></figure>
<p> // 待续</p>

          
        
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      

      

      
      
        <div class="post-eof"></div>
      
    </footer>
  </div>
  
  
  
  </article>


    
  </section>

  
  <nav class="pagination">
    <span class="page-number current">1</span><a class="page-number" href="/blog/page/2/">2</a><span class="space">&hellip;</span><a class="page-number" href="/blog/page/6/">6</a><a class="extend next" rel="next" href="/blog/page/2/"><i class="fa fa-angle-right"></i></a>
  </nav>



          </div>
          


          

        </div>
        
          
  
  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside id="sidebar" class="sidebar">
    
    <div class="sidebar-inner">

      

      

      <section class="site-overview-wrap sidebar-panel sidebar-panel-active">
        <div class="site-overview">
          <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
            
              <img class="site-author-image" itemprop="image"
                src="/blog/images/avatar.jpg"
                alt="Srtian" />
            
              <p class="site-author-name" itemprop="name">Srtian</p>
              <p class="site-description motion-element" itemprop="description">JUST DO IT.</p>
          </div>

          <nav class="site-state motion-element">

            
              <div class="site-state-item site-state-posts">
              
                <a href="/blog/archives/">
              
                  <span class="site-state-item-count">59</span>
                  <span class="site-state-item-name">日志</span>
                </a>
              </div>
            

            
              
              
              <div class="site-state-item site-state-categories">
                <a href="/blog/categories/index.html">
                  <span class="site-state-item-count">15</span>
                  <span class="site-state-item-name">分类</span>
                </a>
              </div>
            

            
              
              
              <div class="site-state-item site-state-tags">
                <a href="/blog/tags/index.html">
                  <span class="site-state-item-count">37</span>
                  <span class="site-state-item-name">标签</span>
                </a>
              </div>
            

          </nav>

          

          
            <div class="links-of-author motion-element">
                
                  <span class="links-of-author-item">
                    <a href="https://gitee.com/srtian96" target="_blank" title="GitHub">
                      
                        <i class="fa fa-fw fa-github"></i>GitHub</a>
                  </span>
                
                  <span class="links-of-author-item">
                    <a href="shenruotian@gmail.com" target="_blank" title="E-Mail">
                      
                        <i class="fa fa-fw fa-envelope"></i>E-Mail</a>
                  </span>
                
            </div>
          

          
          

          
          

          

        </div>
      </section>

      

      

    </div>
  </aside>


        
      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner">
        <div class="copyright">&copy; <span itemprop="copyrightYear">2019</span>
  <span class="with-love">
    <i class="fa fa-user"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">Srtian</span>
<script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js">
</script>
  
    <span class="post-meta-divider">|</span>
    <span class="post-meta-item-icon">
      <i class="fa fa-area-chart"></i>
    </span>
    
      <span class="post-meta-item-text">Site words total count&#58;</span>
    
    <span title="Site words total count">102.8k</span>
  
</div>





        
<div class="busuanzi-count">
  <script async src="https://dn-lbstatics.qbox.me/busuanzi/2.3/busuanzi.pure.mini.js"></script>

  
    <span class="site-uv">
      本站访客数
      <span class="busuanzi-value" id="busuanzi_value_site_uv"></span>
      人次
    </span>
  

  
    <span class="site-pv">
      本站总访问量
      <span class="busuanzi-value" id="busuanzi_value_site_pv"></span>
      次
    </span>
  
</div>








        
      </div>
    </footer>

    
      <div class="back-to-top">
        <i class="fa fa-arrow-up"></i>
        
      </div>
    

    

  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
</script>









  












  
  
    <script type="text/javascript" src="/blog/lib/jquery/index.js?v=2.1.3"></script>
  

  
  
    <script type="text/javascript" src="/blog/lib/fastclick/lib/fastclick.min.js?v=1.0.6"></script>
  

  
  
    <script type="text/javascript" src="/blog/lib/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script>
  

  
  
    <script type="text/javascript" src="/blog/lib/velocity/velocity.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/blog/lib/velocity/velocity.ui.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/blog/lib/fancybox/source/jquery.fancybox.pack.js?v=2.1.5"></script>
  


  


  <script type="text/javascript" src="/blog/js/src/utils.js?v=5.1.4"></script>

  <script type="text/javascript" src="/blog/js/src/motion.js?v=5.1.4"></script>



  
  


  <script type="text/javascript" src="/blog/js/src/affix.js?v=5.1.4"></script>

  <script type="text/javascript" src="/blog/js/src/schemes/pisces.js?v=5.1.4"></script>



  

  


  <script type="text/javascript" src="/blog/js/src/bootstrap.js?v=5.1.4"></script>



  


  




	





  





  












  

  <script type="text/javascript">
    // Popup Window;
    var isfetched = false;
    var isXml = true;
    // Search DB path;
    var search_path = "search.xml";
    if (search_path.length === 0) {
      search_path = "search.xml";
    } else if (/json$/i.test(search_path)) {
      isXml = false;
    }
    var path = "/blog/" + search_path;
    // monitor main search box;

    var onPopupClose = function (e) {
      $('.popup').hide();
      $('#local-search-input').val('');
      $('.search-result-list').remove();
      $('#no-result').remove();
      $(".local-search-pop-overlay").remove();
      $('body').css('overflow', '');
    }

    function proceedsearch() {
      $("body")
        .append('<div class="search-popup-overlay local-search-pop-overlay"></div>')
        .css('overflow', 'hidden');
      $('.search-popup-overlay').click(onPopupClose);
      $('.popup').toggle();
      var $localSearchInput = $('#local-search-input');
      $localSearchInput.attr("autocapitalize", "none");
      $localSearchInput.attr("autocorrect", "off");
      $localSearchInput.focus();
    }

    // search function;
    var searchFunc = function(path, search_id, content_id) {
      'use strict';

      // start loading animation
      $("body")
        .append('<div class="search-popup-overlay local-search-pop-overlay">' +
          '<div id="search-loading-icon">' +
          '<i class="fa fa-spinner fa-pulse fa-5x fa-fw"></i>' +
          '</div>' +
          '</div>')
        .css('overflow', 'hidden');
      $("#search-loading-icon").css('margin', '20% auto 0 auto').css('text-align', 'center');

      $.ajax({
        url: path,
        dataType: isXml ? "xml" : "json",
        async: true,
        success: function(res) {
          // get the contents from search data
          isfetched = true;
          $('.popup').detach().appendTo('.header-inner');
          var datas = isXml ? $("entry", res).map(function() {
            return {
              title: $("title", this).text(),
              content: $("content",this).text(),
              url: $("url" , this).text()
            };
          }).get() : res;
          var input = document.getElementById(search_id);
          var resultContent = document.getElementById(content_id);
          var inputEventFunction = function() {
            var searchText = input.value.trim().toLowerCase();
            var keywords = searchText.split(/[\s\-]+/);
            if (keywords.length > 1) {
              keywords.push(searchText);
            }
            var resultItems = [];
            if (searchText.length > 0) {
              // perform local searching
              datas.forEach(function(data) {
                var isMatch = false;
                var hitCount = 0;
                var searchTextCount = 0;
                var title = data.title.trim();
                var titleInLowerCase = title.toLowerCase();
                var content = data.content.trim().replace(/<[^>]+>/g,"");
                var contentInLowerCase = content.toLowerCase();
                var articleUrl = decodeURIComponent(data.url);
                var indexOfTitle = [];
                var indexOfContent = [];
                // only match articles with not empty titles
                if(title != '') {
                  keywords.forEach(function(keyword) {
                    function getIndexByWord(word, text, caseSensitive) {
                      var wordLen = word.length;
                      if (wordLen === 0) {
                        return [];
                      }
                      var startPosition = 0, position = [], index = [];
                      if (!caseSensitive) {
                        text = text.toLowerCase();
                        word = word.toLowerCase();
                      }
                      while ((position = text.indexOf(word, startPosition)) > -1) {
                        index.push({position: position, word: word});
                        startPosition = position + wordLen;
                      }
                      return index;
                    }

                    indexOfTitle = indexOfTitle.concat(getIndexByWord(keyword, titleInLowerCase, false));
                    indexOfContent = indexOfContent.concat(getIndexByWord(keyword, contentInLowerCase, false));
                  });
                  if (indexOfTitle.length > 0 || indexOfContent.length > 0) {
                    isMatch = true;
                    hitCount = indexOfTitle.length + indexOfContent.length;
                  }
                }

                // show search results

                if (isMatch) {
                  // sort index by position of keyword

                  [indexOfTitle, indexOfContent].forEach(function (index) {
                    index.sort(function (itemLeft, itemRight) {
                      if (itemRight.position !== itemLeft.position) {
                        return itemRight.position - itemLeft.position;
                      } else {
                        return itemLeft.word.length - itemRight.word.length;
                      }
                    });
                  });

                  // merge hits into slices

                  function mergeIntoSlice(text, start, end, index) {
                    var item = index[index.length - 1];
                    var position = item.position;
                    var word = item.word;
                    var hits = [];
                    var searchTextCountInSlice = 0;
                    while (position + word.length <= end && index.length != 0) {
                      if (word === searchText) {
                        searchTextCountInSlice++;
                      }
                      hits.push({position: position, length: word.length});
                      var wordEnd = position + word.length;

                      // move to next position of hit

                      index.pop();
                      while (index.length != 0) {
                        item = index[index.length - 1];
                        position = item.position;
                        word = item.word;
                        if (wordEnd > position) {
                          index.pop();
                        } else {
                          break;
                        }
                      }
                    }
                    searchTextCount += searchTextCountInSlice;
                    return {
                      hits: hits,
                      start: start,
                      end: end,
                      searchTextCount: searchTextCountInSlice
                    };
                  }

                  var slicesOfTitle = [];
                  if (indexOfTitle.length != 0) {
                    slicesOfTitle.push(mergeIntoSlice(title, 0, title.length, indexOfTitle));
                  }

                  var slicesOfContent = [];
                  while (indexOfContent.length != 0) {
                    var item = indexOfContent[indexOfContent.length - 1];
                    var position = item.position;
                    var word = item.word;
                    // cut out 100 characters
                    var start = position - 20;
                    var end = position + 80;
                    if(start < 0){
                      start = 0;
                    }
                    if (end < position + word.length) {
                      end = position + word.length;
                    }
                    if(end > content.length){
                      end = content.length;
                    }
                    slicesOfContent.push(mergeIntoSlice(content, start, end, indexOfContent));
                  }

                  // sort slices in content by search text's count and hits' count

                  slicesOfContent.sort(function (sliceLeft, sliceRight) {
                    if (sliceLeft.searchTextCount !== sliceRight.searchTextCount) {
                      return sliceRight.searchTextCount - sliceLeft.searchTextCount;
                    } else if (sliceLeft.hits.length !== sliceRight.hits.length) {
                      return sliceRight.hits.length - sliceLeft.hits.length;
                    } else {
                      return sliceLeft.start - sliceRight.start;
                    }
                  });

                  // select top N slices in content

                  var upperBound = parseInt('1');
                  if (upperBound >= 0) {
                    slicesOfContent = slicesOfContent.slice(0, upperBound);
                  }

                  // highlight title and content

                  function highlightKeyword(text, slice) {
                    var result = '';
                    var prevEnd = slice.start;
                    slice.hits.forEach(function (hit) {
                      result += text.substring(prevEnd, hit.position);
                      var end = hit.position + hit.length;
                      result += '<b class="search-keyword">' + text.substring(hit.position, end) + '</b>';
                      prevEnd = end;
                    });
                    result += text.substring(prevEnd, slice.end);
                    return result;
                  }

                  var resultItem = '';

                  if (slicesOfTitle.length != 0) {
                    resultItem += "<li><a href='" + articleUrl + "' class='search-result-title'>" + highlightKeyword(title, slicesOfTitle[0]) + "</a>";
                  } else {
                    resultItem += "<li><a href='" + articleUrl + "' class='search-result-title'>" + title + "</a>";
                  }

                  slicesOfContent.forEach(function (slice) {
                    resultItem += "<a href='" + articleUrl + "'>" +
                      "<p class=\"search-result\">" + highlightKeyword(content, slice) +
                      "...</p>" + "</a>";
                  });

                  resultItem += "</li>";
                  resultItems.push({
                    item: resultItem,
                    searchTextCount: searchTextCount,
                    hitCount: hitCount,
                    id: resultItems.length
                  });
                }
              })
            };
            if (keywords.length === 1 && keywords[0] === "") {
              resultContent.innerHTML = '<div id="no-result"><i class="fa fa-search fa-5x" /></div>'
            } else if (resultItems.length === 0) {
              resultContent.innerHTML = '<div id="no-result"><i class="fa fa-frown-o fa-5x" /></div>'
            } else {
              resultItems.sort(function (resultLeft, resultRight) {
                if (resultLeft.searchTextCount !== resultRight.searchTextCount) {
                  return resultRight.searchTextCount - resultLeft.searchTextCount;
                } else if (resultLeft.hitCount !== resultRight.hitCount) {
                  return resultRight.hitCount - resultLeft.hitCount;
                } else {
                  return resultRight.id - resultLeft.id;
                }
              });
              var searchResultList = '<ul class=\"search-result-list\">';
              resultItems.forEach(function (result) {
                searchResultList += result.item;
              })
              searchResultList += "</ul>";
              resultContent.innerHTML = searchResultList;
            }
          }

          if ('auto' === 'auto') {
            input.addEventListener('input', inputEventFunction);
          } else {
            $('.search-icon').click(inputEventFunction);
            input.addEventListener('keypress', function (event) {
              if (event.keyCode === 13) {
                inputEventFunction();
              }
            });
          }

          // remove loading animation
          $(".local-search-pop-overlay").remove();
          $('body').css('overflow', '');

          proceedsearch();
        }
      });
    }

    // handle and trigger popup window;
    $('.popup-trigger').click(function(e) {
      e.stopPropagation();
      if (isfetched === false) {
        searchFunc(path, 'local-search-input', 'local-search-result');
      } else {
        proceedsearch();
      };
    });

    $('.popup-btn-close').click(onPopupClose);
    $('.popup').click(function(e){
      e.stopPropagation();
    });
    $(document).on('keyup', function (event) {
      var shouldDismissSearchPopup = event.which === 27 &&
        $('.search-popup').is(':visible');
      if (shouldDismissSearchPopup) {
        onPopupClose();
      }
    });
  </script>





  

  

  

  
  

  

  

  

</body>
</html>
